Glad I came across this thread, and can't thank @mpboyle enough for posting his code almost 9 years ago. I looked at the document he used and a similar one several years ago, but got lost in the math.
I had a problem where I wanted to use the same Arcade expression in ArcGIS Pro as well as AGOL to display Latitude/Longitude in a popup, but since there is no support for reprojection in Arcade, I converted this SQL Server code to arcade. The code below checks the spatial reference and converts a point coordinate to Lat/Lon from either Cal SP Zone 5 or Web Mercator. Bear in mind that for other states and zones you would have to replace the False Easting/Northing, Standard Parallels, and Latitude of Origin with appropriate values, and that all this only applies to states that use Lambert Conic for their Spate Plane projection.
//Stolen from: https://community.esri.com/t5/coordinate-reference-systems-questions/formula-for-state-plane-to-lat-lon-conversion/td-p/870543/page/2
function CalSP5ToLatLon(dblX, dblY) {
//// Projection Parameters
var dblPi = PI
var dblFX = 6561666.666666666 //False Easting
var dblFY = 1640416.666666667 //False Northing
var dblCmd = -118.0 //Central Meridian
var dblCmr = dblCmd * (dblPi / 180.0)
var dblSP1d = 34.03333333333333 //Standard Parallel 1
var dblSP1r = dblSP1d * (dblPi / 180.0)
var dblSP2d = 35.46666666666667 //Standard Parallel 2
var dblSP2r = dblSP2d * (dblPi / 180.0)
var dblLOd = 33.5 //Latitude of Origin
var dblLOr = dblLOd * (dblPi / 180.0)
var dblSmjrM = 6378137.0 //Semi Major Axis meters
var dblSmjrF = dblSmjrM * 3.2808333
var dblSmnrM = 6356752.314140356 //Semi Minor Axis meters
var dblSmnrF = dblSmnrM * 3.2808333
var dblIflat = dblSmjrM / (dblSmjrM - dblSmnrM) // Inverse Flattening
var dblFlat = 1 / dblIflat
var dblE = Pow((2 * dblFlat - Pow(dblFlat,2)) , 0.5)
var dblM1 = Cos(dblSP1r) / Sqrt((1 - Pow(dblE, 2) * Pow(Sin(dblSP1r), 2)))
var dblM2 = Cos(dblSP2r) / Sqrt((1 - Pow(dblE, 2) * Pow(Sin(dblSP2r), 2)))
var dblT1 = Tan((dblPi / 4) - (dblSP1r / 2)) / Pow((1 - (dblE * Sin(dblSP1r))) / (1 + (dblE * Sin(dblSP1r))), (dblE / 2))
var dblT2 = Tan((dblPi / 4) - (dblSP2r / 2)) / Pow((1 - (dblE * Sin(dblSP2r))) / (1 + (dblE * Sin(dblSP2r))), (dblE / 2))
var dblTF = TAN((dblPi / 4) - (dblLOr / 2)) / Pow((1 - (dblE * SIN(dblLOr))) / (1 + (dblE * SIN(dblLOr))), (dblE / 2))
var dblN = (Log(dblM1) - Log(dblM2)) / (Log(dblT1) - Log(dblT2))
var dblF = dblM1 / (dblN * Pow(dblT1, dblN))
var dblRF = dblSmjrF * dblF * Pow(dblTF, dblN)
var dblRZ = Pow(Pow((dblX - dblFX), 2) + Pow((dblRF - (dblY - dblFY)), 2), 0.5)
var dblTZ = Pow((dblRZ / (dblSmjrF * dblF)), (1 / dblN))
var dblZZ = Atan((dblX - dblFX) / (dblRF - (dblY - dblFY)))
//// Calc Longitude
var dblLonR = ((dblZZ / dblN) + dblCmr)
var dblLonD = (dblLonR * 180) / dblPi
//// Calc Latitude
var dblLatTR = (dblPi / 2) - 2 * Atan(dblTZ)
var dblLat1R = (dblPi / 2) - (2 * Atan((dblTZ * Pow((1 - (dblE * SIN(dblLatTR))) / (1 + (dblE * Sin(dblLatTR))), (dblE / 2)))))
var dblLat2R = (dblPi / 2) - (2 * Atan((dblTZ * Pow((1 - (dblE * SIN(dblLat1R))) / (1 + (dblE * Sin(dblLat1R))), (dblE / 2)))))
var dblLat3R = (dblPi / 2) - (2 * Atan((dblTZ * Pow((1 - (dblE * SIN(dblLat2R))) / (1 + (dblE * Sin(dblLat2R))), (dblE / 2)))))
var dblLat4R = (dblPi / 2) - (2 * Atan((dblTZ * Pow((1 - (dblE * SIN(dblLat3R))) / (1 + (dblE * Sin(dblLat3R))), (dblE / 2)))))
var dblLatFR = (dblPi / 2) - (2 * Atan((dblTZ * Pow((1 - (dblE * SIN(dblLat4R))) / (1 + (dblE * Sin(dblLat4R))), (dblE / 2)))))
var dblLatFD = (dblLatFR * 180) / dblPi
return [dblLatFD, dblLonD]
}
function WebMercToLatLon(x, y) {
// Converts XY point from Spherical Mercator EPSG:900913 to lat/lon in WGS84 Datum
// Fuente: http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/
var originShift = 2.0 * PI * 6378137.0 / 2.0;
var lon = (x / originShift) * 180.0;
var lat = (y / originShift) * 180.0;
lat = 180.0 / PI * (2.0 * Atan( Exp( lat * PI / 180.0)) - PI / 2.0);
return [lat, lon];
}
var LatLon;
var strOut;
var x = Geometry($feature).x;
var y = Geometry($feature).y;
var spatRef = Geometry($feature).spatialReference;
if (spatRef['wkid'] == 102100) {
LatLon = WebMercToLatLon(x, y);
strOut = 'Latitude: ' + LatLon[0] + '<BR>' + 'Longitude: ' + LatLon[1];
}else if (spatRef['wkid'] == 2229) {
LatLon = CalSP5ToLatLon(x, y);
strOut = 'Latitude: ' + LatLon[0] + '<BR>' + 'Longitude: ' + LatLon[1];
}else{
strOut = 'Unknown Spatial Reference: ' + spatRef;
}
return {
type : 'text',
//text : 'Latitude: '
//text : 'Latitude: ' + LonLat[0] + '<BR>' + 'Longitude: ' + LonLat[1] //this property supports html tags
//text : x + '<BR>' + y
text : strOut
}