Closed ted-howard closed 3 years ago
I just joined GitHub and this may not be the right place. I have been working with the get address from centerline code and it is working with my data. However, I would like to pull additional fields from the centerline to populate fields in my address point layer. Some of those fields would be contingent on the side of the road the point is on (ESN, County, etc.). Do you know where I can find some sample code for that?
@lingal1968 pulling additional fields from the centerline is simple. Request the fields you want here: https://github.com/Esri/arcade-expressions/blob/186c6efb6ccddbd787dd0dbb4eab4951f2679c41/attribute_rule_calculation/GetAddressFromCenterline.js#L17
And then include them in the payload here: https://github.com/Esri/arcade-expressions/blob/186c6efb6ccddbd787dd0dbb4eab4951f2679c41/attribute_rule_calculation/GetAddressFromCenterline.js#L214-L217
Determining county etc is more complex. I would point you to the Address Data Management solution which includes a lot of complex attribute rules for this sort of thing. Some of the examples are found in this repo here
I was overthinking it. Thank you.
As for the county, esn, etc. I can’t figure out how to pull the data from the road centerline based on the side_of_line value of either right or left. The fields are in the centerline layer, (ex: CountyLeft and CountyRight) but only one would copy to the structure based on which side of the road it's on. I just don't know how to filter based on that calculated value.
I've looked at the Address Data Management and I can't find anything that would help.
I can't thank you enough for the guidance. Have a good evening.
Get Outlook for iOShttps://aka.ms/o0ukef
From: Ted Howard @.> Sent: Tuesday, April 30, 2024 7:27:20 PM To: Esri/arcade-expressions @.> Cc: Linda Gallion @.>; Mention @.> Subject: Re: [Esri/arcade-expressions] update variable section (#37)
@lingal1968https://github.com/lingal1968 pulling additional fields from the centerline is simple. Request the fields you want here: https://github.com/Esri/arcade-expressions/blob/186c6efb6ccddbd787dd0dbb4eab4951f2679c41/attribute_rule_calculation/GetAddressFromCenterline.js#L17
And then include them in the payload here: https://github.com/Esri/arcade-expressions/blob/186c6efb6ccddbd787dd0dbb4eab4951f2679c41/attribute_rule_calculation/GetAddressFromCenterline.js#L214-L217
Determining county etc is more complex. I would point you to the Address Data Managementhttps://doc.arcgis.com/en/arcgis-solutions/latest/reference/introduction-to-address-data-management.htm solution which includes a lot of complex attribute rules for this sort of thing. Some of the examples are found in this repo herehttps://github.com/Esri/arcade-expressions/tree/186c6efb6ccddbd787dd0dbb4eab4951f2679c41/Industry/AddressManagement
— Reply to this email directly, view it on GitHubhttps://github.com/Esri/arcade-expressions/pull/37#issuecomment-2087765085, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BIGH3WF75GBDM7PM6IBQY73ZAAZGRAVCNFSM42Z4QGPKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBYG43TMNJQHA2Q. You are receiving this because you were mentioned.Message ID: @.***>
if those fields are in centerline then all you need is to inspect the return from closest_point_info https://github.com/Esri/arcade-expressions/blob/186c6efb6ccddbd787dd0dbb4eab4951f2679c41/attribute_rule_calculation/GetAddressFromCenterline.js#L195
data['lineSide']
will tell you left or right
I’m sorry. I don’t understand. Could you provide a sample?
Get Outlook for iOShttps://aka.ms/o0ukef
From: Ted Howard @.> Sent: Tuesday, April 30, 2024 7:53:09 PM To: Esri/arcade-expressions @.> Cc: Linda Gallion @.>; Mention @.> Subject: Re: [Esri/arcade-expressions] update variable section (#37)
if those fields are in centerline then all you need is to inspect the return from closest_point_info https://github.com/Esri/arcade-expressions/blob/186c6efb6ccddbd787dd0dbb4eab4951f2679c41/attribute_rule_calculation/GetAddressFromCenterline.js#L195
data['lineSide'] will tell you left or right
— Reply to this email directly, view it on GitHubhttps://github.com/Esri/arcade-expressions/pull/37#issuecomment-2087785944, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BIGH3WANT3SIYA4OVMVQ3J3ZAA4HLAVCNFSM42Z4QGPKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBYG43TQNJZGQ2A. You are receiving this because you were mentioned.Message ID: @.***>
Assuming request CountyLeft and CountyRight fields for the centerline
// return result to update attributes of $feature
return {
"result": {
"attributes":
Dictionary(
percent_field, percent_along,
offdir_field, data["lineSide"],
address_field, address_num,
fullname_field, closest_line[fullname],
county, Iif(data["lineSide"] == "left", CountyLeft, CountyRight)
)
}
}
Perfect. Thank you.
Get Outlook for iOShttps://aka.ms/o0ukef
From: Ted Howard @.> Sent: Tuesday, April 30, 2024 8:05:32 PM To: Esri/arcade-expressions @.> Cc: Linda Gallion @.>; Mention @.> Subject: Re: [Esri/arcade-expressions] update variable section (#37)
Assuming request CountyLeft and CountyRight fields for the centerline
// return result to update attributes of $feature return { "result": { "attributes": Dictionary( percent_field, percent_along, offdir_field, data["lineSide"], address_field, address_num, fullname_field, closest_line[fullname] county, Iif(data["lineSide"] == "left", CountyLeft, CountyRight) ) } }
— Reply to this email directly, view it on GitHubhttps://github.com/Esri/arcade-expressions/pull/37#issuecomment-2087795693, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BIGH3WHSWNUGFA6QGLUFG2LZAA5VZAVCNFSM42Z4QGPKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBYG43TSNJWHEZQ. You are receiving this because you were mentioned.Message ID: @.***>
Good afternoon. It’s recognizing the fields as text and returning CountyLeft instead of the value of the field. I’ve tried a few ways but I’m not having any luck. What should I change to make it recognize this as a field? – Also, I’m not having any luck adding the additional fields in the results section – it gives me a “wrong number of arguments”. I’ve attached the whole code. The msagpredir_field is the additional field I am trying to add (just the first of several I’ll need to include). Any guidance is greatly appreciated!
// This rule will calculate an address number for a point based on nearest road centerline
// Centerline attribute field names var fromleft = "fromleft" var toleft = "toleft" var fromright = "fromright" var toright = "toright" var fullname = "msagcomple" var msagpredir = "msagpredir" var countyleft = "countyleft" var countyrt = "countyrt" var commleft = "commleft" var commright = "commright" var esnleft = "esnleft" var esnright = "esnright" var zipleft = "zipleft" var zipright = "zipright"
// Site Address Point fields to calculate var percent_field = "PERCENTALONG" // percent along centerline where site address lies var offdir_field = "OFFDIR" // side of centerline where site address lies var fullname_field = "msagcomple" // name of closest centerline var address_field = "address" // address number calculated based on where site address lies var county_field = "county" var msagcommun_field = "msagcommun" var esn_field = "esn" var zipcode_field = "zipcode" var msagpredir_field = "msagpredir"
// The Road Centerline feature set var line_class = FeatureSetByName($datastore, "regional_branch.sde.WCTCOGRoadCenterlines", [fromleft, toleft, fromright, toright, fullname, msagpredir, countyleft, countyrt, commleft, commright, esnleft, esnright, zipleft, zipright], true);
// Functions
function find_closest_line() { // Find closest line segment to $feature. Limit search to specific radius. var candidates = Intersects(line_class, Buffer($feature, 1000, "feet")); //var candidates = line_class;
var shortest = [1e10, null];
for (var line in candidates) {
var d = Distance($feature, line)
if (d < shortest[0]) shortest = [d, line]
}
return shortest[-1]
}
function closest_point_info(point_feature, line_feature) { /* finds the closest point on line_feature from point_feature
Args:
point_feature: Point Geometry
line_feature: Line Geometry
Returns: dictionary
{distance: number, // distance from point_feature to closest point
coordinates: array, // the coordinate pair of the closest point
isVertex: bool, // if the closest point is a vertex of line_feature
lineSide: text} // side of the line that point_feature is on based
*/
var point_feature = Geometry(point_feature);
var line_feature = Geometry(line_feature);
var vertices = line_feature["paths"]
var x = point_feature["x"];
var y = point_feature["y"];
// Loop through each part of the geometry and each segment, tracking the shortest distance
var shortest = [1e10];
for (var i in vertices) {
var part = vertices[i];
var previous = part[0];
for (var j = 1; j < Count(part); j++) {
var current = part[j];
var result = pDistance(x, y, previous["x"], previous["y"], current["x"], current["y"]);
if (result[0] < shortest[0]) shortest = result
previous = current;
}
}
// Couldn't find anything
if (Count(shortest) == 1) return null
return {"distance": shortest[0],
"coordinates": shortest[1],
"isVertex": shortest[2],
"lineSide": shortest[3]}
}
function pDistance(x, y, x1, y1, x2, y2) { // adopted from https://stackoverflow.com/a/6853926 var A = x - x1; var B = y - y1; var C = x2 - x1; var D = y2 - y1;
var dot = A C + B D; var len_sq = C C + D D; var param = -1; if (len_sq != 0) //in case of 0 length line param = dot / len_sq;
var xx, yy; var is_vertex = true; if (param < 0) { xx = x1; yy = y1; } else if (param > 1) { xx = x2; yy = y2; } else { is_vertex = false; xx = x1 + param C; yy = y1 + param D; }
var dx = x - xx; var dy = y - yy; return [Sqrt(dx dx + dy dy), [xx, yy], is_vertex, side_of_line(x,y,x1,y1,x2,y2)]; }
function side_of_line(x, y, x1, y1, x2, y2) { // get side of line segment that a point (x, y) is on based on the direction of segment [[x1, y1], [x2, y2]] // adopted from https://math.stackexchange.com/a/274728 var d = (x - x1) (y2 - y1) - (y - y1) (x2 - x1) if (d < 0) { return 'left' } else if (d > 0) { return 'right' } else { return null } }
function intersect_distance_along(intersect_geometry, line, unit){ // Loop through the segments of the line. Handle multipart geometries. var distance_along_line = 0; for (var part in Geometry(line).paths) { var segment = Geometry(line).paths[part];
// Loop through the points in the segment
for (var i in segment) {
if (i == 0) continue;
// Construct a 2-point line segment from the current and previous point
var first_point = segment[i-1];
var second_point = segment[i]
var two_point_line = Polyline({'paths': [[[first_point.x, first_point.y], [second_point.x, second_point.y]]], 'spatialReference' : first_point.spatialReference});
// Test if the point intersects the 2-point line segment
if (Intersects(intersect_geometry, two_point_line)) {
// Construct a 2-point line segment using the previous point and the address point
var last_segment = Polyline({'paths': [[[first_point.x, first_point.y], [intersect_geometry.x, intersect_geometry.y]]], 'spatialReference' : first_point.spatialReference});
// Add to the total distance along the line and break the loop
distance_along_line += Length(last_segment, unit);
return distance_along_line
}
// Add to the toal distance along the line
distance_along_line += Length(two_point_line, unit);
}
}
return null;
}
function create_point(coordinates, spatial_ref) { // create point geometry from coordinates [x, y] return Point({"x": coordinates[0], "y": coordinates[1], "spatialReference": spatial_ref}) }
function get_addr_num(road, percent_along, dir) { // This function will return the address number of the new site address point // It determines this based on the from and to address range on the intersecting road and the direction of the offset // If direction is null, defaults to left offset var addr_num = null; if (IsEmpty(dir)) dir = 'left'; var from = road[fromleft]; var to = road[toleft]; if (Lower(dir) == 'right') { var from = road[fromright]; var to = road[toright]; } if (from == null || to == null) return null; var val = percent_along * (to - from); var addr_num = 0;
if ((Floor(val) % 2) == 0) addr_num = Floor(val);
else if ((Ceil(val) % 2) == 0) addr_num = Ceil(val);
else addr_num = Floor(val) - 1;
return from + addr_num;
}
// ***** End Functions Section **
// find closest line to $feature var closest_line = find_closest_line(); if (closest_line == null) return
// find info about the closest point on the closest line to $feature var data = closest_point_info($feature, closest_line); if (data == null) return
// calculate the distance along of closest point var closest_point = create_point(data["coordinates"], Geometry($feature)["spatialReference"]) var distance_along = intersect_distance_along(closest_point, closest_line, "feet") if (distance_along == null) return { "errorMessage": "could not calculate distance along" } var percent_along = distance_along / Length(closest_line, "feet")
// calculate address number var address_num = get_addr_num(closest_line, percent_along, data["lineSide"])
// return result to update attributes of $feature return { "result": { "attributes": Dictionary( percent_field, percent_along, offdir_field, data["lineSide"], address_field, address_num, fullname_field, closest_line[fullname], county_field, Iif(data["lineSide"] == "left", countyleft, countyrt), msagcommun_field, Iif(data["lineSide"] == "left", commleft, commright), zipcode_field, Iif(data["lineSide"] == "left", zipleft, zipright), esn_field, Iif(data["lineSide"] == "left", esnleft, esnright) ) } }
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Ted Howard @.> Sent: Tuesday, April 30, 2024 8:06 PM To: Esri/arcade-expressions @.> Cc: Linda Gallion @.>; Mention @.> Subject: Re: [Esri/arcade-expressions] update variable section (#37)
Assuming request CountyLeft and CountyRight fields for the centerline
// return result to update attributes of $feature
return {
"result": {
"attributes":
Dictionary(
percent_field, percent_along,
offdir_field, data["lineSide"],
address_field, address_num,
fullname_field, closest_line[fullname]
county, Iif(data["lineSide"] == "left", CountyLeft, CountyRight)
)
}
}
— Reply to this email directly, view it on GitHubhttps://github.com/Esri/arcade-expressions/pull/37#issuecomment-2087795693, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BIGH3WHSWNUGFA6QGLUFG2LZAA5VZAVCNFSM42Z4QGPKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBYG43TSNJWHEZQ. You are receiving this because you were mentioned.Message ID: @.**@.>>
I emailed too quickly. I got the right/left fields to work. I am unable to add additional fields from the road centerline layer. I want to copy not just the [fullname] but also, [msagpredir] and others into fields in my structure layer with the same names -– it gives me a “wrong number of arguments”. What am I doing wrong?
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Linda Gallion Sent: Wednesday, May 1, 2024 2:12 PM To: Esri/arcade-expressions @.>; Esri/arcade-expressions @.> Cc: Mention @.***> Subject: RE: [Esri/arcade-expressions] update variable section (#37)
Good afternoon. It’s recognizing the fields as text and returning CountyLeft instead of the value of the field. I’ve tried a few ways but I’m not having any luck. What should I change to make it recognize this as a field? – Also, I’m not having any luck adding the additional fields in the results section – it gives me a “wrong number of arguments”. I’ve attached the whole code. The msagpredir_field is the additional field I am trying to add (just the first of several I’ll need to include). Any guidance is greatly appreciated!
// This rule will calculate an address number for a point based on nearest road centerline
// Centerline attribute field names var fromleft = "fromleft" var toleft = "toleft" var fromright = "fromright" var toright = "toright" var fullname = "msagcomple" var msagpredir = "msagpredir" var countyleft = "countyleft" var countyrt = "countyrt" var commleft = "commleft" var commright = "commright" var esnleft = "esnleft" var esnright = "esnright" var zipleft = "zipleft" var zipright = "zipright"
// Site Address Point fields to calculate var percent_field = "PERCENTALONG" // percent along centerline where site address lies var offdir_field = "OFFDIR" // side of centerline where site address lies var fullname_field = "msagcomple" // name of closest centerline var address_field = "address" // address number calculated based on where site address lies var county_field = "county" var msagcommun_field = "msagcommun" var esn_field = "esn" var zipcode_field = "zipcode" var msagpredir_field = "msagpredir"
// The Road Centerline feature set var line_class = FeatureSetByName($datastore, "regional_branch.sde.WCTCOGRoadCenterlines", [fromleft, toleft, fromright, toright, fullname, msagpredir, countyleft, countyrt, commleft, commright, esnleft, esnright, zipleft, zipright], true);
// Functions
function find_closest_line() { // Find closest line segment to $feature. Limit search to specific radius. var candidates = Intersects(line_class, Buffer($feature, 1000, "feet")); //var candidates = line_class;
var shortest = [1e10, null];
for (var line in candidates) {
var d = Distance($feature, line)
if (d < shortest[0]) shortest = [d, line]
}
return shortest[-1]
}
function closest_point_info(point_feature, line_feature) { /* finds the closest point on line_feature from point_feature
Args:
point_feature: Point Geometry
line_feature: Line Geometry
Returns: dictionary
{distance: number, // distance from point_feature to closest point
coordinates: array, // the coordinate pair of the closest point
isVertex: bool, // if the closest point is a vertex of line_feature
lineSide: text} // side of the line that point_feature is on based
*/
var point_feature = Geometry(point_feature);
var line_feature = Geometry(line_feature);
var vertices = line_feature["paths"]
var x = point_feature["x"];
var y = point_feature["y"];
// Loop through each part of the geometry and each segment, tracking the shortest distance
var shortest = [1e10];
for (var i in vertices) {
var part = vertices[i];
var previous = part[0];
for (var j = 1; j < Count(part); j++) {
var current = part[j];
var result = pDistance(x, y, previous["x"], previous["y"], current["x"], current["y"]);
if (result[0] < shortest[0]) shortest = result
previous = current;
}
}
// Couldn't find anything
if (Count(shortest) == 1) return null
return {"distance": shortest[0],
"coordinates": shortest[1],
"isVertex": shortest[2],
"lineSide": shortest[3]}
}
function pDistance(x, y, x1, y1, x2, y2) { // adopted from https://stackoverflow.com/a/6853926 var A = x - x1; var B = y - y1; var C = x2 - x1; var D = y2 - y1;
var dot = A C + B D; var len_sq = C C + D D; var param = -1; if (len_sq != 0) //in case of 0 length line param = dot / len_sq;
var xx, yy; var is_vertex = true; if (param < 0) { xx = x1; yy = y1; } else if (param > 1) { xx = x2; yy = y2; } else { is_vertex = false; xx = x1 + param C; yy = y1 + param D; }
var dx = x - xx; var dy = y - yy; return [Sqrt(dx dx + dy dy), [xx, yy], is_vertex, side_of_line(x,y,x1,y1,x2,y2)]; }
function side_of_line(x, y, x1, y1, x2, y2) { // get side of line segment that a point (x, y) is on based on the direction of segment [[x1, y1], [x2, y2]] // adopted from https://math.stackexchange.com/a/274728 var d = (x - x1) (y2 - y1) - (y - y1) (x2 - x1) if (d < 0) { return 'left' } else if (d > 0) { return 'right' } else { return null } }
function intersect_distance_along(intersect_geometry, line, unit){ // Loop through the segments of the line. Handle multipart geometries. var distance_along_line = 0; for (var part in Geometry(line).paths) { var segment = Geometry(line).paths[part];
// Loop through the points in the segment
for (var i in segment) {
if (i == 0) continue;
// Construct a 2-point line segment from the current and previous point
var first_point = segment[i-1];
var second_point = segment[i]
var two_point_line = Polyline({'paths': [[[first_point.x, first_point.y], [second_point.x, second_point.y]]], 'spatialReference' : first_point.spatialReference});
// Test if the point intersects the 2-point line segment
if (Intersects(intersect_geometry, two_point_line)) {
// Construct a 2-point line segment using the previous point and the address point
var last_segment = Polyline({'paths': [[[first_point.x, first_point.y], [intersect_geometry.x, intersect_geometry.y]]], 'spatialReference' : first_point.spatialReference});
// Add to the total distance along the line and break the loop
distance_along_line += Length(last_segment, unit);
return distance_along_line
}
// Add to the toal distance along the line
distance_along_line += Length(two_point_line, unit);
}
}
return null;
}
function create_point(coordinates, spatial_ref) { // create point geometry from coordinates [x, y] return Point({"x": coordinates[0], "y": coordinates[1], "spatialReference": spatial_ref}) }
function get_addr_num(road, percent_along, dir) { // This function will return the address number of the new site address point // It determines this based on the from and to address range on the intersecting road and the direction of the offset // If direction is null, defaults to left offset var addr_num = null; if (IsEmpty(dir)) dir = 'left'; var from = road[fromleft]; var to = road[toleft]; if (Lower(dir) == 'right') { var from = road[fromright]; var to = road[toright]; } if (from == null || to == null) return null; var val = percent_along * (to - from); var addr_num = 0;
if ((Floor(val) % 2) == 0) addr_num = Floor(val);
else if ((Ceil(val) % 2) == 0) addr_num = Ceil(val);
else addr_num = Floor(val) - 1;
return from + addr_num;
}
// ***** End Functions Section **
// find closest line to $feature var closest_line = find_closest_line(); if (closest_line == null) return
// find info about the closest point on the closest line to $feature var data = closest_point_info($feature, closest_line); if (data == null) return
// calculate the distance along of closest point var closest_point = create_point(data["coordinates"], Geometry($feature)["spatialReference"]) var distance_along = intersect_distance_along(closest_point, closest_line, "feet") if (distance_along == null) return { "errorMessage": "could not calculate distance along" } var percent_along = distance_along / Length(closest_line, "feet")
// calculate address number var address_num = get_addr_num(closest_line, percent_along, data["lineSide"])
// return result to update attributes of $feature return { "result": { "attributes": Dictionary( percent_field, percent_along, offdir_field, data["lineSide"], address_field, address_num, fullname_field, closest_line[fullname], county_field, Iif(data["lineSide"] == "left", countyleft, countyrt), msagcommun_field, Iif(data["lineSide"] == "left", commleft, commright), zipcode_field, Iif(data["lineSide"] == "left", zipleft, zipright), esn_field, Iif(data["lineSide"] == "left", esnleft, esnright) ) } }
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Ted Howard @.**@.>> Sent: Tuesday, April 30, 2024 8:06 PM To: Esri/arcade-expressions @.**@.>> Cc: Linda Gallion @.**@.>>; Mention @.**@.>> Subject: Re: [Esri/arcade-expressions] update variable section (#37)
Assuming request CountyLeft and CountyRight fields for the centerline
// return result to update attributes of $feature
return {
"result": {
"attributes":
Dictionary(
percent_field, percent_along,
offdir_field, data["lineSide"],
address_field, address_num,
fullname_field, closest_line[fullname]
county, Iif(data["lineSide"] == "left", CountyLeft, CountyRight)
)
}
}
— Reply to this email directly, view it on GitHubhttps://github.com/Esri/arcade-expressions/pull/37#issuecomment-2087795693, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BIGH3WHSWNUGFA6QGLUFG2LZAA5VZAVCNFSM42Z4QGPKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBYG43TSNJWHEZQ. You are receiving this because you were mentioned.Message ID: @.**@.>>
Disregard. I figured this out as well. Thank you for all of your assistance!
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Linda Gallion Sent: Wednesday, May 1, 2024 3:48 PM To: Esri/arcade-expressions @.>; Esri/arcade-expressions @.> Cc: Mention @.***> Subject: RE: [Esri/arcade-expressions] update variable section (#37)
I emailed too quickly. I got the right/left fields to work. I am unable to add additional fields from the road centerline layer. I want to copy not just the [fullname] but also, [msagpredir] and others into fields in my structure layer with the same names -– it gives me a “wrong number of arguments”. What am I doing wrong?
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Linda Gallion Sent: Wednesday, May 1, 2024 2:12 PM To: Esri/arcade-expressions @.**@.>>; Esri/arcade-expressions @.**@.>> Cc: Mention @.**@.>> Subject: RE: [Esri/arcade-expressions] update variable section (#37)
Good afternoon. It’s recognizing the fields as text and returning CountyLeft instead of the value of the field. I’ve tried a few ways but I’m not having any luck. What should I change to make it recognize this as a field? – Also, I’m not having any luck adding the additional fields in the results section – it gives me a “wrong number of arguments”. I’ve attached the whole code. The msagpredir_field is the additional field I am trying to add (just the first of several I’ll need to include). Any guidance is greatly appreciated!
// This rule will calculate an address number for a point based on nearest road centerline
// Centerline attribute field names var fromleft = "fromleft" var toleft = "toleft" var fromright = "fromright" var toright = "toright" var fullname = "msagcomple" var msagpredir = "msagpredir" var countyleft = "countyleft" var countyrt = "countyrt" var commleft = "commleft" var commright = "commright" var esnleft = "esnleft" var esnright = "esnright" var zipleft = "zipleft" var zipright = "zipright"
// Site Address Point fields to calculate var percent_field = "PERCENTALONG" // percent along centerline where site address lies var offdir_field = "OFFDIR" // side of centerline where site address lies var fullname_field = "msagcomple" // name of closest centerline var address_field = "address" // address number calculated based on where site address lies var county_field = "county" var msagcommun_field = "msagcommun" var esn_field = "esn" var zipcode_field = "zipcode" var msagpredir_field = "msagpredir"
// The Road Centerline feature set var line_class = FeatureSetByName($datastore, "regional_branch.sde.WCTCOGRoadCenterlines", [fromleft, toleft, fromright, toright, fullname, msagpredir, countyleft, countyrt, commleft, commright, esnleft, esnright, zipleft, zipright], true);
// Functions
function find_closest_line() { // Find closest line segment to $feature. Limit search to specific radius. var candidates = Intersects(line_class, Buffer($feature, 1000, "feet")); //var candidates = line_class;
var shortest = [1e10, null];
for (var line in candidates) {
var d = Distance($feature, line)
if (d < shortest[0]) shortest = [d, line]
}
return shortest[-1]
}
function closest_point_info(point_feature, line_feature) { /* finds the closest point on line_feature from point_feature
Args:
point_feature: Point Geometry
line_feature: Line Geometry
Returns: dictionary
{distance: number, // distance from point_feature to closest point
coordinates: array, // the coordinate pair of the closest point
isVertex: bool, // if the closest point is a vertex of line_feature
lineSide: text} // side of the line that point_feature is on based
*/
var point_feature = Geometry(point_feature);
var line_feature = Geometry(line_feature);
var vertices = line_feature["paths"]
var x = point_feature["x"];
var y = point_feature["y"];
// Loop through each part of the geometry and each segment, tracking the shortest distance
var shortest = [1e10];
for (var i in vertices) {
var part = vertices[i];
var previous = part[0];
for (var j = 1; j < Count(part); j++) {
var current = part[j];
var result = pDistance(x, y, previous["x"], previous["y"], current["x"], current["y"]);
if (result[0] < shortest[0]) shortest = result
previous = current;
}
}
// Couldn't find anything
if (Count(shortest) == 1) return null
return {"distance": shortest[0],
"coordinates": shortest[1],
"isVertex": shortest[2],
"lineSide": shortest[3]}
}
function pDistance(x, y, x1, y1, x2, y2) { // adopted from https://stackoverflow.com/a/6853926 var A = x - x1; var B = y - y1; var C = x2 - x1; var D = y2 - y1;
var dot = A C + B D; var len_sq = C C + D D; var param = -1; if (len_sq != 0) //in case of 0 length line param = dot / len_sq;
var xx, yy; var is_vertex = true; if (param < 0) { xx = x1; yy = y1; } else if (param > 1) { xx = x2; yy = y2; } else { is_vertex = false; xx = x1 + param C; yy = y1 + param D; }
var dx = x - xx; var dy = y - yy; return [Sqrt(dx dx + dy dy), [xx, yy], is_vertex, side_of_line(x,y,x1,y1,x2,y2)]; }
function side_of_line(x, y, x1, y1, x2, y2) { // get side of line segment that a point (x, y) is on based on the direction of segment [[x1, y1], [x2, y2]] // adopted from https://math.stackexchange.com/a/274728 var d = (x - x1) (y2 - y1) - (y - y1) (x2 - x1) if (d < 0) { return 'left' } else if (d > 0) { return 'right' } else { return null } }
function intersect_distance_along(intersect_geometry, line, unit){ // Loop through the segments of the line. Handle multipart geometries. var distance_along_line = 0; for (var part in Geometry(line).paths) { var segment = Geometry(line).paths[part];
// Loop through the points in the segment
for (var i in segment) {
if (i == 0) continue;
// Construct a 2-point line segment from the current and previous point
var first_point = segment[i-1];
var second_point = segment[i]
var two_point_line = Polyline({'paths': [[[first_point.x, first_point.y], [second_point.x, second_point.y]]], 'spatialReference' : first_point.spatialReference});
// Test if the point intersects the 2-point line segment
if (Intersects(intersect_geometry, two_point_line)) {
// Construct a 2-point line segment using the previous point and the address point
var last_segment = Polyline({'paths': [[[first_point.x, first_point.y], [intersect_geometry.x, intersect_geometry.y]]], 'spatialReference' : first_point.spatialReference});
// Add to the total distance along the line and break the loop
distance_along_line += Length(last_segment, unit);
return distance_along_line
}
// Add to the toal distance along the line
distance_along_line += Length(two_point_line, unit);
}
}
return null;
}
function create_point(coordinates, spatial_ref) { // create point geometry from coordinates [x, y] return Point({"x": coordinates[0], "y": coordinates[1], "spatialReference": spatial_ref}) }
function get_addr_num(road, percent_along, dir) { // This function will return the address number of the new site address point // It determines this based on the from and to address range on the intersecting road and the direction of the offset // If direction is null, defaults to left offset var addr_num = null; if (IsEmpty(dir)) dir = 'left'; var from = road[fromleft]; var to = road[toleft]; if (Lower(dir) == 'right') { var from = road[fromright]; var to = road[toright]; } if (from == null || to == null) return null; var val = percent_along * (to - from); var addr_num = 0;
if ((Floor(val) % 2) == 0) addr_num = Floor(val);
else if ((Ceil(val) % 2) == 0) addr_num = Ceil(val);
else addr_num = Floor(val) - 1;
return from + addr_num;
}
// ***** End Functions Section **
// find closest line to $feature var closest_line = find_closest_line(); if (closest_line == null) return
// find info about the closest point on the closest line to $feature var data = closest_point_info($feature, closest_line); if (data == null) return
// calculate the distance along of closest point var closest_point = create_point(data["coordinates"], Geometry($feature)["spatialReference"]) var distance_along = intersect_distance_along(closest_point, closest_line, "feet") if (distance_along == null) return { "errorMessage": "could not calculate distance along" } var percent_along = distance_along / Length(closest_line, "feet")
// calculate address number var address_num = get_addr_num(closest_line, percent_along, data["lineSide"])
// return result to update attributes of $feature return { "result": { "attributes": Dictionary( percent_field, percent_along, offdir_field, data["lineSide"], address_field, address_num, fullname_field, closest_line[fullname], county_field, Iif(data["lineSide"] == "left", countyleft, countyrt), msagcommun_field, Iif(data["lineSide"] == "left", commleft, commright), zipcode_field, Iif(data["lineSide"] == "left", zipleft, zipright), esn_field, Iif(data["lineSide"] == "left", esnleft, esnright) ) } }
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Ted Howard @.**@.>> Sent: Tuesday, April 30, 2024 8:06 PM To: Esri/arcade-expressions @.**@.>> Cc: Linda Gallion @.**@.>>; Mention @.**@.>> Subject: Re: [Esri/arcade-expressions] update variable section (#37)
Assuming request CountyLeft and CountyRight fields for the centerline
// return result to update attributes of $feature
return {
"result": {
"attributes":
Dictionary(
percent_field, percent_along,
offdir_field, data["lineSide"],
address_field, address_num,
fullname_field, closest_line[fullname]
county, Iif(data["lineSide"] == "left", CountyLeft, CountyRight)
)
}
}
— Reply to this email directly, view it on GitHubhttps://github.com/Esri/arcade-expressions/pull/37#issuecomment-2087795693, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BIGH3WHSWNUGFA6QGLUFG2LZAA5VZAVCNFSM42Z4QGPKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBYG43TSNJWHEZQ. You are receiving this because you were mentioned.Message ID: @.**@.>>
I have another pretty big coding problem. I do the 9-1-1 addressing for an 18-county area and have to deal with existing addresses (which aren’t always accurate). Geocoding new addresses is not an option as I have to work within the constraints of existing flawed addressing. I’m not sure where to even start. Is this something you’ve seen? Can you point me to some sample code that I can work with or someone who specializes in this complexity of coding? I’ve spelled out what I want to achieve below but I’m just not sure if it’s even possible. Any guidance you can provide is greatly appreciated.
Auto assign addresses using existing structure points and road centerlines before trying to geocode. This is the order to follow when determining a new address:
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Linda Gallion Sent: Wednesday, May 1, 2024 3:55 PM To: Esri/arcade-expressions @.>; Esri/arcade-expressions @.> Cc: Mention @.***> Subject: RE: [Esri/arcade-expressions] update variable section (#37)
Disregard. I figured this out as well. Thank you for all of your assistance!
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Linda Gallion Sent: Wednesday, May 1, 2024 3:48 PM To: Esri/arcade-expressions @.**@.>>; Esri/arcade-expressions @.**@.>> Cc: Mention @.**@.>> Subject: RE: [Esri/arcade-expressions] update variable section (#37)
I emailed too quickly. I got the right/left fields to work. I am unable to add additional fields from the road centerline layer. I want to copy not just the [fullname] but also, [msagpredir] and others into fields in my structure layer with the same names -– it gives me a “wrong number of arguments”. What am I doing wrong?
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Linda Gallion Sent: Wednesday, May 1, 2024 2:12 PM To: Esri/arcade-expressions @.**@.>>; Esri/arcade-expressions @.**@.>> Cc: Mention @.**@.>> Subject: RE: [Esri/arcade-expressions] update variable section (#37)
Good afternoon. It’s recognizing the fields as text and returning CountyLeft instead of the value of the field. I’ve tried a few ways but I’m not having any luck. What should I change to make it recognize this as a field? – Also, I’m not having any luck adding the additional fields in the results section – it gives me a “wrong number of arguments”. I’ve attached the whole code. The msagpredir_field is the additional field I am trying to add (just the first of several I’ll need to include). Any guidance is greatly appreciated!
// This rule will calculate an address number for a point based on nearest road centerline
// Centerline attribute field names var fromleft = "fromleft" var toleft = "toleft" var fromright = "fromright" var toright = "toright" var fullname = "msagcomple" var msagpredir = "msagpredir" var countyleft = "countyleft" var countyrt = "countyrt" var commleft = "commleft" var commright = "commright" var esnleft = "esnleft" var esnright = "esnright" var zipleft = "zipleft" var zipright = "zipright"
// Site Address Point fields to calculate var percent_field = "PERCENTALONG" // percent along centerline where site address lies var offdir_field = "OFFDIR" // side of centerline where site address lies var fullname_field = "msagcomple" // name of closest centerline var address_field = "address" // address number calculated based on where site address lies var county_field = "county" var msagcommun_field = "msagcommun" var esn_field = "esn" var zipcode_field = "zipcode" var msagpredir_field = "msagpredir"
// The Road Centerline feature set var line_class = FeatureSetByName($datastore, "regional_branch.sde.WCTCOGRoadCenterlines", [fromleft, toleft, fromright, toright, fullname, msagpredir, countyleft, countyrt, commleft, commright, esnleft, esnright, zipleft, zipright], true);
// Functions
function find_closest_line() { // Find closest line segment to $feature. Limit search to specific radius. var candidates = Intersects(line_class, Buffer($feature, 1000, "feet")); //var candidates = line_class;
var shortest = [1e10, null];
for (var line in candidates) {
var d = Distance($feature, line)
if (d < shortest[0]) shortest = [d, line]
}
return shortest[-1]
}
function closest_point_info(point_feature, line_feature) { /* finds the closest point on line_feature from point_feature
Args:
point_feature: Point Geometry
line_feature: Line Geometry
Returns: dictionary
{distance: number, // distance from point_feature to closest point
coordinates: array, // the coordinate pair of the closest point
isVertex: bool, // if the closest point is a vertex of line_feature
lineSide: text} // side of the line that point_feature is on based
*/
var point_feature = Geometry(point_feature);
var line_feature = Geometry(line_feature);
var vertices = line_feature["paths"]
var x = point_feature["x"];
var y = point_feature["y"];
// Loop through each part of the geometry and each segment, tracking the shortest distance
var shortest = [1e10];
for (var i in vertices) {
var part = vertices[i];
var previous = part[0];
for (var j = 1; j < Count(part); j++) {
var current = part[j];
var result = pDistance(x, y, previous["x"], previous["y"], current["x"], current["y"]);
if (result[0] < shortest[0]) shortest = result
previous = current;
}
}
// Couldn't find anything
if (Count(shortest) == 1) return null
return {"distance": shortest[0],
"coordinates": shortest[1],
"isVertex": shortest[2],
"lineSide": shortest[3]}
}
function pDistance(x, y, x1, y1, x2, y2) { // adopted from https://stackoverflow.com/a/6853926 var A = x - x1; var B = y - y1; var C = x2 - x1; var D = y2 - y1;
var dot = A C + B D; var len_sq = C C + D D; var param = -1; if (len_sq != 0) //in case of 0 length line param = dot / len_sq;
var xx, yy; var is_vertex = true; if (param < 0) { xx = x1; yy = y1; } else if (param > 1) { xx = x2; yy = y2; } else { is_vertex = false; xx = x1 + param C; yy = y1 + param D; }
var dx = x - xx; var dy = y - yy; return [Sqrt(dx dx + dy dy), [xx, yy], is_vertex, side_of_line(x,y,x1,y1,x2,y2)]; }
function side_of_line(x, y, x1, y1, x2, y2) { // get side of line segment that a point (x, y) is on based on the direction of segment [[x1, y1], [x2, y2]] // adopted from https://math.stackexchange.com/a/274728 var d = (x - x1) (y2 - y1) - (y - y1) (x2 - x1) if (d < 0) { return 'left' } else if (d > 0) { return 'right' } else { return null } }
function intersect_distance_along(intersect_geometry, line, unit){ // Loop through the segments of the line. Handle multipart geometries. var distance_along_line = 0; for (var part in Geometry(line).paths) { var segment = Geometry(line).paths[part];
// Loop through the points in the segment
for (var i in segment) {
if (i == 0) continue;
// Construct a 2-point line segment from the current and previous point
var first_point = segment[i-1];
var second_point = segment[i]
var two_point_line = Polyline({'paths': [[[first_point.x, first_point.y], [second_point.x, second_point.y]]], 'spatialReference' : first_point.spatialReference});
// Test if the point intersects the 2-point line segment
if (Intersects(intersect_geometry, two_point_line)) {
// Construct a 2-point line segment using the previous point and the address point
var last_segment = Polyline({'paths': [[[first_point.x, first_point.y], [intersect_geometry.x, intersect_geometry.y]]], 'spatialReference' : first_point.spatialReference});
// Add to the total distance along the line and break the loop
distance_along_line += Length(last_segment, unit);
return distance_along_line
}
// Add to the toal distance along the line
distance_along_line += Length(two_point_line, unit);
}
}
return null;
}
function create_point(coordinates, spatial_ref) { // create point geometry from coordinates [x, y] return Point({"x": coordinates[0], "y": coordinates[1], "spatialReference": spatial_ref}) }
function get_addr_num(road, percent_along, dir) { // This function will return the address number of the new site address point // It determines this based on the from and to address range on the intersecting road and the direction of the offset // If direction is null, defaults to left offset var addr_num = null; if (IsEmpty(dir)) dir = 'left'; var from = road[fromleft]; var to = road[toleft]; if (Lower(dir) == 'right') { var from = road[fromright]; var to = road[toright]; } if (from == null || to == null) return null; var val = percent_along * (to - from); var addr_num = 0;
if ((Floor(val) % 2) == 0) addr_num = Floor(val);
else if ((Ceil(val) % 2) == 0) addr_num = Ceil(val);
else addr_num = Floor(val) - 1;
return from + addr_num;
}
// ***** End Functions Section **
// find closest line to $feature var closest_line = find_closest_line(); if (closest_line == null) return
// find info about the closest point on the closest line to $feature var data = closest_point_info($feature, closest_line); if (data == null) return
// calculate the distance along of closest point var closest_point = create_point(data["coordinates"], Geometry($feature)["spatialReference"]) var distance_along = intersect_distance_along(closest_point, closest_line, "feet") if (distance_along == null) return { "errorMessage": "could not calculate distance along" } var percent_along = distance_along / Length(closest_line, "feet")
// calculate address number var address_num = get_addr_num(closest_line, percent_along, data["lineSide"])
// return result to update attributes of $feature return { "result": { "attributes": Dictionary( percent_field, percent_along, offdir_field, data["lineSide"], address_field, address_num, fullname_field, closest_line[fullname], county_field, Iif(data["lineSide"] == "left", countyleft, countyrt), msagcommun_field, Iif(data["lineSide"] == "left", commleft, commright), zipcode_field, Iif(data["lineSide"] == "left", zipleft, zipright), esn_field, Iif(data["lineSide"] == "left", esnleft, esnright) ) } }
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Ted Howard @.**@.>> Sent: Tuesday, April 30, 2024 8:06 PM To: Esri/arcade-expressions @.**@.>> Cc: Linda Gallion @.**@.>>; Mention @.**@.>> Subject: Re: [Esri/arcade-expressions] update variable section (#37)
Assuming request CountyLeft and CountyRight fields for the centerline
// return result to update attributes of $feature
return {
"result": {
"attributes":
Dictionary(
percent_field, percent_along,
offdir_field, data["lineSide"],
address_field, address_num,
fullname_field, closest_line[fullname]
county, Iif(data["lineSide"] == "left", CountyLeft, CountyRight)
)
}
}
— Reply to this email directly, view it on GitHubhttps://github.com/Esri/arcade-expressions/pull/37#issuecomment-2087795693, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BIGH3WHSWNUGFA6QGLUFG2LZAA5VZAVCNFSM42Z4QGPKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBYG43TSNJWHEZQ. You are receiving this because you were mentioned.Message ID: @.**@.>>
Seems achievable with existing data by using a batch attribute rule, though it will take a lot of work. Again, I can only point you to the Address Data Management solution. You could also ask Arcade questions on esri community. https://community.esri.com/t5/attribute-rules/ct-p/attribute-rules
Ok. Thank you.
Linda Gallion, 9-1-1 GIS Specialist West Central TX COG 3702 Loop 322 Abilene, TX 79602 (325) 672-8544 From: Ted Howard @.> Sent: Thursday, May 2, 2024 11:09 AM To: Esri/arcade-expressions @.> Cc: Linda Gallion @.>; Mention @.> Subject: Re: [Esri/arcade-expressions] update variable section (#37)
Seems achievable with existing data by using a batch attribute rule, though it will take a lot of work. Again, I can only point you to the Address Data Management solution. You could also ask Arcade questions on esri community. https://community.esri.com/t5/attribute-rules/ct-p/attribute-rules
— Reply to this email directly, view it on GitHubhttps://github.com/Esri/arcade-expressions/pull/37#issuecomment-2090903548, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BIGH3WFFHD6JXVWXZ63E7DTZAJQKBAVCNFSM42Z4QGPKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBZGA4TAMZVGQ4A. You are receiving this because you were mentioned.Message ID: @.**@.>>
Please do not comment on this merged PR anymore. I would suggest you open a new issue if you are looking for help with an attribute rule.