I've converted Dimension Annotation to points and have spatially joined the points with parcel lines based on a Match Option, Within a Distance of 30 feet. This allowed me to get the Dimension Annotation TextString field into the parcel line attributes, which I can then use for labeling purposes. To further this process, I'd like to know if there's a way to know which side of the parcel line the point falls on, so that I could take advantage of using the label position field that's part of the parcel fabric.
ArcGIS Pro ver. 3.1.2
Data is currently in a FGDB
Thanks,
Robert
Solved! Go to Solution.
@MikeMillerGIS was kind enough to modify his arcade expression which was originally suggested by HungGi.
Mike's expression (listed below) was able to do what I needed and will help out going forward with working on migrating dimension annotation to labels. All I needed to do was replace the "LineFC" with my line feature class, then insert the expression in the Calculate Field on my point feature class. I can use this info to assign the label position so that the label remains consistent with the side of the parcel line where the annotation was located.
// The Road Centerline feature set
var line_class = FeatureSetByName($datastore, "LineFC", ["OBJECTID"], true);
// ************* Functions *************
function find_closest_line() {
// Find closest line segment to $feature. Limit search to specific radius.
var candidates = Intersects(line_class, Buffer($feature, 30, "feet"));
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 return_side_of_Line(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 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
}
}
// ************* 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 side_line = return_side_of_Line($feature, closest_line);
if (side_line == null) return
return side_line
Hi @RobertChaney ,
The following link might lead you onto the path to finding your solution: https://github.com/Esri/arcade-expressions/blob/master/attribute_rule_calculation/GetAddressFromCent...
If you look at the attribute rule code on line 114, there is a function that returns which side of the line the point falls on. The result is pushed to a field for the result. I know it's more for address and centreline but i'm pretty sure you might be able to go through the code and extract what you need to figure out which side the point falls on for the parcel line.
Hope this helps!
Great to see that you are improving efficiency by moving away from annotation to labels.
From looking around you can use the arcpy method ' queryPointAndDistance'
It is documented here:
https://pro.arcgis.com/en/pro-app/latest/arcpy/classes/polyline.htm
You can also view this code sample copy - pasted from here:
pnt = [x[0] for x in arcpy.da.SearchCursor("point",["SHAPE@"])][0]
line = [x[0] for x in arcpy.da.SearchCursor("line",["SHAPE@"])][0]
line.queryPointAndDistance(pnt)
#output
(<PointGeometry object at 0x27b71d30[0x27b71860]>, 212.1234, 23.1234, False)
Another place to look at is this community post which concludes the use of the same method.
Once you have it working it might be good to share with others that run into the same issue and want to leverage the LabelPostion field.
@MikeMillerGIS was kind enough to modify his arcade expression which was originally suggested by HungGi.
Mike's expression (listed below) was able to do what I needed and will help out going forward with working on migrating dimension annotation to labels. All I needed to do was replace the "LineFC" with my line feature class, then insert the expression in the Calculate Field on my point feature class. I can use this info to assign the label position so that the label remains consistent with the side of the parcel line where the annotation was located.
// The Road Centerline feature set
var line_class = FeatureSetByName($datastore, "LineFC", ["OBJECTID"], true);
// ************* Functions *************
function find_closest_line() {
// Find closest line segment to $feature. Limit search to specific radius.
var candidates = Intersects(line_class, Buffer($feature, 30, "feet"));
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 return_side_of_Line(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 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
}
}
// ************* 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 side_line = return_side_of_Line($feature, closest_line);
if (side_line == null) return
return side_line