Arcade has an obvious function to convert a measure value to a coordinate, but can the reverse be done? The closest I can find is PointToCoordinate but that claims to return the planar distance, not M values. I'm trying to get the chainage of a nearby running line when a point feature is added or moved in an attribute rule and I need the measured distance specifically.
I don't know if I'm misinterpreting your question but this thread discusses deriving the M value using Arcade. This does work as I'm now using it in a map for my users to click on a road to determine the roadlog milepost location. Outside of this, you need to shell out some $$$ for ESRI's Roads and Highways product which introduces click for M value and zoom to M value functionality which has not made it into their standard web map products to date.
Thanks for pointing me to that thread! Combining that with some of my own thinking, this should return a very accurate M interpolation for a popup, which I can then adapt to my attribute rule:
var geom = Geometry($feature)
var paths = null;
var hasCurves = null;
if (HasValue(geom, "curvePaths")) {
paths = geom.curvePaths;
hasCurves = true;
} else {
paths = geom.paths;
hasCurves = false;
}
var coord = PointToCoordinate(geom, $userInput)
var start = paths[coord.partId][coord.segmentID];
var end = paths[coord.partId][coord.segmentID + 1];
var seg = null;
if (hasCurves) {
seg = Polyline({
"curvePaths": [[start, end]],
hasZ: geom.hasZ,
hasM: true,
spatialReference: geom.spatialReference
});
} else {
seg = Polyline({
"paths": [[start, end]],
hasZ: geom.hasZ,
hasM: true,
spatialReference: geom.spatialReference
});
}
var percent = (PointToCoordinate(seg, $userInput).distanceAlong) / Length(seg);
var lerp = ((end.m - start.m) * percent) + start.m;
return Text(lerp)
Turns out proper curve support requires some real work, here's a better listing:
var geom = Geometry($feature);
var geom_dict = Dictionary(Text(geom));
var paths = DefaultValue(geom_dict, "curvePaths", geom.paths);
var m_idx = Iif(geom.hasZ, 3, 2);
var coord = PointToCoordinate(geom, $userInput);
var start = paths[coord.partId][coord.segmentID];
var first = null;
if (HasKey(start, "a")) first = start["a"][0];
else if (HasKey(start, "b")) first = start["b"][0];
else if (HasKey(start, "c")) first = start["c"][0];
if (!IsEmpty(first)) {
start = Point({
"x": first[0],
"y": first[1],
"z": Iif(geom.hasZ, first[2], null),
"m": first[m_idx],
"spatialReference": geom.spatialReference
});
}
var end = paths[coord.partId][coord.segmentID + 1];
var seg = Polyline({
"curvePaths": [[start, end]],
hasZ: geom.hasZ,
hasM: true,
spatialReference: geom.spatialReference
});
var start_m = start.m;
var end_m = null;
if (HasKey(end, "a")) end_m = end["a"][0][m_idx];
else if (HasKey(end, "b")) end_m = end["b"][0][m_idx];
else if (HasKey(end, "c")) end_m = end["c"][0][m_idx];
else end_m = end.m;
var percent = (PointToCoordinate(seg, $userInput).distanceAlong) / Length(seg);
var lerp = ((end_m - start_m) * percent) + start_m;
return Text(lerp)