Select to view content in your preferred language

Arcade: Get Measure from Coordinate?

116
3
2 weeks ago
Labels (1)
DavidSolari
MVP Regular Contributor

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.

0 Kudos
3 Replies
SteveCole
Honored Contributor

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.

https://community.esri.com/t5/arcgis-pro-questions/get-route-m-value-at-point-clicked-using-arcade/m... 

DavidSolari
MVP Regular Contributor

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)
0 Kudos
DavidSolari
MVP Regular Contributor

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)