Select to view content in your preferred language

Is there a way to automatically update latitude and longitude attribute fields, possibly using attribute rules?

365
5
Jump to solution
4 weeks ago
MOBrien2
Occasional Contributor

I've been trying to use attribute rules to update the X and Y coordinate fields as well as the longitude and latitude values of an attribute table whenever the address points I work with are moved or when new points are added. I've been able to follow this Esri blog post to get the X and Y fields to update automatically via attribute rules and in the state plane coordinate system used, but I haven't had any success trying to do the same thing only with latitude and longitude values, in decimal degrees. Using the Calculate Geometry Attributes tool has worked to update the latitude & longitude values when it's run, but I was looking for any way to accomplish the same result but performed every time a feature is moved/added.

edit: I should clarify that I'm using state plane New York East (WKID 2260), which uses Transverse Mercator projection and lacks the standard parallels found in east-west state plane coordinate systems.

1 Solution

Accepted Solutions
MOBrien2
Occasional Contributor

I realized today that the central meridian of my state plane was being used in a longitude equation that expects it to have a positive value, when it's being entered as a negative value. For now, I've changed the script to add the central meridian at the longitude calculation instead of subtracting values from it. The longitude values are much closer to what was expected, but it's possible they're not as close as the values the NGS NCAT or ArcGIS Pro tools would produce. I won't mark this as solved unless the data I use is found to have a sufficient level of accuracy after some testing.

After doing some testing, this script has produced point coordinates which average about 1.0000000567736929E-8 degrees away from their original location. This is accurate enough for address points within or close to the state plane zone. I'll mark this as a solution, though I hope that the projectAs geometry function gets implemented in Arcade.

The corrected script has been posted below:

//Transverse Mercator projected state plane coordinates converted to geodetic coordinates using these equations should be accurate to the milimeter or submilimeter. If modifying this script, test its accuracy before implementing.

// === Projection parameters for NAD83 State Plane New York East FIPS 3101 (US Survey Feet) (WKID 2260)===
//Change the variables in this section and others if using a different state plane or ellipsoid.

//US Survey foot compared to meters
var linear_unit = 0.3048006096012192; 
//Latitude of origin (decimal degrees) (38°50'N)
//var latitude_of_origin = 38.8333333333333;
//Commented out as this script does not rely on the latitude of origin
//Longitude of origin and Central Meridian (decimal degrees) (74°30'W)
var central_meridian = -74.5;
//Scale factor at natural origin/central meridian
var scale_factor_origin = 0.999900;
//False Easting (meters) (492125.0 in US Survey Feet)
var false_easting = 150000.0;
//False Northing (meters)
var false_northing = 0.0; 
//Meridional distance from the equator to latitude of origin, multiplied by the central meridian scale factor (written as S subscript o in documentation) (meters)
var meridional_distance_origin = 4299571.6693;

//GRS80 ellipsoid
var semimajor_axis = 6378137.0; 
//in meters
var semiminor_axis = 6356752.314140356; 
//in meters

//Helper functions
function degToRad(deg){return (deg*PI/180)};
function radToDeg(rad){return (rad*180/PI)};
function USsurveyFtToMeters(ft){return ft*linear_unit};

//Convert projection information into radians
var cmRad = degToRad(central_meridian);

//== Ellipsoid definitions ==
//Flattening of the ellipsoid
var f = (semimajor_axis - semiminor_axis) / semimajor_axis;
//Inverse flattening, should be 298.25722210088 or close to it
//var invFlat = 1 / f; //Commented out as no other expressions use it in this script
//First eccentricity squared, should be 0.0066943800229034 or close to it
var ecc_sq = 2*f-Pow(f,2);
//Second eccentricity squared
var secondEcc_sq = ecc_sq/(1-ecc_sq);

//Equations and constants adapted from https://geodesy.noaa.gov/library/pdfs/NOAA_Manual_NOS_NGS_0005.pdf
//REMEMBER TO CHECK IF LONGITUDE/X-COORDINATE FORMULA AND CENTRAL MERIDIAN USES POSITIVE WEST OR POSITIVE EAST!
//https://geodesy.noaa.gov/library/pdfs/Transverse_Mercator_Projection_Tables_New_York.pdf
//Projection constants
var r_s = 6367449.14577;//Radius of the rectifying sphere (GRS 80, meters)
var V_0 = 0.005022893948;
var V_2 = 0.000029370625;
var V_4 = 0.000000235059;
var V_6 = 0.000000002181;

function reverseTM(X,Y){
//Equations taken from and variables based on those used in chapter 3.2, Transverse Mercator Mapping Equations, of NOAA Manual NOS NGS 5, State Plane Coordinate System of 1983, linked above
//Linear units in these equations are the units of false northing/easting and ellipsoid (meters)
//All angles are in radians
var X_m = USsurveyFtToMeters(X);
var Y_m = USsurveyFtToMeters(Y);
var rectifying_latitude = (Y_m - false_northing + meridional_distance_origin)/(scale_factor_origin*r_s);
var footprint_lat = rectifying_latitude + (sin(rectifying_latitude)*cos(rectifying_latitude)) * (V_0 + Pow(cos(rectifying_latitude),2) * (V_2 + Pow(cos(rectifying_latitude),2) * (V_4 + V_6 * Pow(cos(rectifying_latitude),2))));
var footprint_R = scale_factor_origin * semimajor_axis / Pow(1 - ecc_sq * Pow(sin(footprint_lat),2),0.5);
var Q = (X_m - false_easting) / footprint_R;
var t_f = tan(footprint_lat); //tangent of footprint latitude
var Eta2_f = secondEcc_sq*Pow(cos(footprint_lat),2); //lowercase Eta squared, using footprint latitude
var B_2 = -0.5*t_f*(1+Eta2_f);
var B_4 = (-1/12)*(5+(3*Pow(t_f,2))+(Eta2_f*(1-(9*Pow(t_f,2))))-(4*Pow(Eta2_f,2)));
var B_6 = (1/360)*(61+(90*Pow(t_f,2))+(45*Pow(t_f,4))+(Eta2_f*(46-(252*Pow(t_f,2))-(90*Pow(t_f,4)))));
//latitude in radians; positive is north
var latitude_rad = footprint_lat+B_2*Pow(Q,2)*(Pow(Q,2)*(B_6*Pow(Q,2)+B_4)+1);
var B_3 = (-1/6)*(1+(2*Pow(t_f,2))+Eta2_f);
var B_5 = (1/120)*(5+(28*Pow(t_f,2))+(24*Pow(t_f,4))+(Eta2_f*(8*Pow(t_f,2)+6)));
var B_7 = (-1/5040)*(61+(662*Pow(t_f,2))+(1320*Pow(t_f,4))+(720*Pow(t_f,6)));
var L = Q*(Pow(Q,2)*(Pow(Q,2)*(B_7*Pow(Q,2)+B_5)+B_3)+1);
//longitude in radians, positive is east; longitude_rad should use subtraction instead of addition if central_meridian is positive.
var longitude_rad = cmRad+(L/cos(footprint_lat));

var Lat = radToDeg(latitude_rad);
var Lon = radToDeg(longitude_rad);

return [Lat,Lon];
}

// === Get point coordinates from the feature ===
var x = Geometry($feature).x;
var y = Geometry($feature).y;

//Convert to Lat/Lon
var latlon = reverseTM(x,y);

//Output for attribute rules:
return{
        "result":{
             "attributes":{
                   "LAT_CALCULATED": latlon[0],
                   "LON_CALCULATED": latlon[1] //Use the field names for latitude and longitude in the double quotes
                   }
                }
}

 

View solution in original post

0 Kudos
5 Replies
Robert_LeClair
Esri Esteemed Contributor

Yes there is but it requires a bit of arcade script that I discovered from this thread - https://community.esri.com/t5/arcgis-pro-ideas/arcade-projectas-geometry-function/idi-p/1171382/page...  - from Tom Neer (page 2).

I created a point feature class for airports in Northern CO in CO State Plane North (2231) with 2 attribute fields - one called latitude and one called longitude.  I created 2 immediate calculation rules for lat / long and only changed the last line where in the arcade expression it says change me.  Should be obvious.  Then I added a new point and moved it - this triggered an immediate calculation for the lat/long field.  Seems to work!

For your projected coordinate system, you'll need to change line 4 and 9-17 to make it work for something else than CO State Plane North - let me know how it goes!

MOBrien2
Occasional Contributor

Thank you for the Arcade script, @Robert_LeClair. I should have clarified that I'm using state plane New York East (WKID 2260), and it seems as though others in the thread that you linked were having the same difficulty that I am in converting the script to work with Transverse Mercator projection, as it doesn't use standard parallels.

I had also found something similar in this post (https://community.esri.com/t5/arcgis-pro-questions/automatically-calculate-geometry-of-points-in-web...) and tried to adapt it based on the documentation for Transverse Mercator (using an older version of the original document rather than the transcription found at this link) but kept getting inaccurate results (off by about 7 degrees or so, according to the existing values in my data and confirmed by using the NGS NCAT conversion tool). However, I was testing my adapted script in Arcade's Playground with static values rather than using test data and attribute rules in Pro directly, maybe there was some issue there?

0 Kudos
MOBrien2
Occasional Contributor

I've been looking through the following documents (https://geodesy.noaa.gov/library/pdfs/NOAA_Manual_NOS_NGS_0005.pdf) (https://geodesy.noaa.gov/library/pdfs/Transverse_Mercator_Projection_Tables_New_York.pdf) and trying to adapt the script to use their methods for Transverse Mercator and New York State Plane East (state plane code 3101). I've had issues with the longitude, but have performed a few tests that gave a very close latitude value. I can't seem to find any resources elsewhere online that handle this specific state plane and that explain their method for calculating geodetic coordinates.

Here is my script so far:

//This script currently doesn't produce correct values, check to ensure it works and edit the return output lines at the end before using in attribute rules!

// === Projection parameters for NAD83 State Plane New York East FIPS 3101 (US Survey Feet) (WKID 2260)===
var linear_unit = 0.3048006096012192; //US Survey foot compared to meters
//var latitude_of_origin = 38.8333333333333; //Latitude of origin (decimal degrees) (30°50'N degrees-minutes) (written as uppercase Phi subscript o in the documentation linked below)
var central_meridian = -74.5; //Longitude of origin and Central Meridian (decimal degrees) (74°30'W degrees-minutes) (written as Lambda subscript o in the documentation)
var scale_factor_origin = 0.999900; //Scale factor at natural origin (central meridian) (written as k subscript o in documentation)
var false_easting = 150000.0; //False Easting (meters) (492125.0 in US Survey Feet)
var false_northing = 0.0; //False Northing (meters/US Survey Feet)
var meridional_distance_origin = 4299571.6693; /*Meridional distance from the equator to latitude of origin, multiplied by the central meridian scale factor (written as S subscript o in documentation) (meters)*/

// GRS80 ellipsoid
var semimajor_axis = 6378137.0; //semi-major axis (meters)
var semiminor_axis = 6356752.314140356; //(meters)

//Helper functions
function degToRad(deg){return (deg*PI/180)};
function radToDeg(rad){return (rad*180/PI)};
function USsurveyFtToMeters(ft){return ft*linear_unit};

//Convert projection information into radians
var cmRad = degToRad(central_meridian);

//Ellipsoid
var f = (semimajor_axis - semiminor_axis) / semimajor_axis;/*Flattening of the ellipsoid*/
var invFlat = 1 / f; /*Inverse flattening, should be 298.25722210088 or close to it*/
var ecc_sq = 2*f-Pow(f,2);//First eccentricity squared, should be 0.0066943800229034 or close to it
var secondEcc_sq = ecc_sq/(1-ecc_sq);/*Second eccentricity squared*/

//Equations and constants adapted from https://geodesy.noaa.gov/library/pdfs/NOAA_Manual_NOS_NGS_0005.pdf
//REMEMBER TO CHECK IF LONGITUDE/X-COORDINATE FORMULA USES POSITIVE WEST OR POSITIVE EAST!!!
//https://geodesy.noaa.gov/library/pdfs/Transverse_Mercator_Projection_Tables_New_York.pdf
//Projection constants
var r_s = 6367449.14577;//Radius of the rectifying sphere (GRS 80, meters)
var V_0 = 0.005022893948;
var V_2 = 0.000029370625;
var V_4 = 0.000000235059;
var V_6 = 0.000000002181;

function reverseTM(X,Y){
//Equations taken from and variables based on those used in chapter 3.2, Transverse Mercator Mapping Equations, of NOAA Manual NOS NGS 5, State Plane Coordinate System of 1983, linked above
//Linear units in these equations are the units of false northing/easting and ellipsoid (meters, unless all values in this script are converted to US survey feet)
//Angles are in radians
var X_m = USsurveyFtToMeters(X);
var Y_m = USsurveyFtToMeters(Y);
var rectifying_latitude = (Y_m - false_northing + meridional_distance_origin)/(scale_factor_origin*r_s);
var footprint_lat = rectifying_latitude + (sin(rectifying_latitude)*cos(rectifying_latitude)) * (V_0 + Pow(cos(rectifying_latitude),2) * (V_2 + Pow(cos(rectifying_latitude),2) * (V_4 + V_6 * Pow(cos(rectifying_latitude),2))));
var footprint_R = scale_factor_origin * semimajor_axis / Pow(1 - ecc_sq * Pow(sin(footprint_lat),2),0.5);
var E_d = X_m - false_easting;//Easting derivative
var Q = E_d / footprint_R;
var t_f = tan(footprint_lat); //tangent of footprint latitude
var Eta2_f = secondEcc_sq*Pow(cos(footprint_lat),2); //lowercase Eta squared, using footprint latitude
var B_2 = -0.5*t_f*(1+Eta2_f);
var B_4 = (-1/12)*(5+(3*Pow(t_f,2))+(Eta2_f*(1-(9*Pow(t_f,2))))-(4*Pow(Eta2_f,2)));
var B_6 = (1/360)*(61+(90*Pow(t_f,2))+(45*Pow(t_f,4))+(Eta2_f*(46-(252*Pow(t_f,2))-(90*Pow(t_f,4)))));
var latitude_rad = B_2*Pow(Q,2)*(Pow(Q,2)*(B_6*Pow(Q,2)+B_4)+1)+footprint_lat;//in radians; positive is north
var B_3 = (-1/6)*(1+(2*Pow(t_f,2))+Eta2_f);
var B_5 = (1/120)*(5+(28*Pow(t_f,2))+(24*Pow(t_f,4))+(Eta2_f*(8*Pow(t_f,2)+6)));
var B_7 = (-1/5040)*(61+(662*Pow(t_f,2))+(1320*Pow(t_f,4))+(720*Pow(t_f,6)));
var L = Q*(Pow(Q,2)*(Pow(Q,2)*(B_7*Pow(Q,2)+B_5)+B_3)+1);
var longitude_rad = cmRad-(L/cos(footprint_lat));/*in radians; positive is east according to testing*/

var Lat = radToDeg(latitude_rad);
var Lon = radToDeg(longitude_rad);

return [Lat,Lon];
}

// === Get point coordinates from the feature ===
//Use known coordinates here instead to test the script before implementing into attribute rules
var x = Geometry($feature).x;
var y = Geometry($feature).y;

//Convert to Lat/Lon
var latlon = reverseTM(x,y);

//Format output as text for testing, actual output for attribute rules wiil be commented out below
return Concatenate('Lat: ',latlon[0],', Lon: ',latlon[1],' ');

//Output for attribute rules:
/*return{
        "result":{
             "attributes":{
                   "LAT_CALCULATED": latlon[0],
                   "LON_CALCULATED": latlon[1] //Use the field names for latitude and longitude in the double quotes
                   }
                }
      }*/

 

0 Kudos
MOBrien2
Occasional Contributor

I realized today that the central meridian of my state plane was being used in a longitude equation that expects it to have a positive value, when it's being entered as a negative value. For now, I've changed the script to add the central meridian at the longitude calculation instead of subtracting values from it. The longitude values are much closer to what was expected, but it's possible they're not as close as the values the NGS NCAT or ArcGIS Pro tools would produce. I won't mark this as solved unless the data I use is found to have a sufficient level of accuracy after some testing.

After doing some testing, this script has produced point coordinates which average about 1.0000000567736929E-8 degrees away from their original location. This is accurate enough for address points within or close to the state plane zone. I'll mark this as a solution, though I hope that the projectAs geometry function gets implemented in Arcade.

The corrected script has been posted below:

//Transverse Mercator projected state plane coordinates converted to geodetic coordinates using these equations should be accurate to the milimeter or submilimeter. If modifying this script, test its accuracy before implementing.

// === Projection parameters for NAD83 State Plane New York East FIPS 3101 (US Survey Feet) (WKID 2260)===
//Change the variables in this section and others if using a different state plane or ellipsoid.

//US Survey foot compared to meters
var linear_unit = 0.3048006096012192; 
//Latitude of origin (decimal degrees) (38°50'N)
//var latitude_of_origin = 38.8333333333333;
//Commented out as this script does not rely on the latitude of origin
//Longitude of origin and Central Meridian (decimal degrees) (74°30'W)
var central_meridian = -74.5;
//Scale factor at natural origin/central meridian
var scale_factor_origin = 0.999900;
//False Easting (meters) (492125.0 in US Survey Feet)
var false_easting = 150000.0;
//False Northing (meters)
var false_northing = 0.0; 
//Meridional distance from the equator to latitude of origin, multiplied by the central meridian scale factor (written as S subscript o in documentation) (meters)
var meridional_distance_origin = 4299571.6693;

//GRS80 ellipsoid
var semimajor_axis = 6378137.0; 
//in meters
var semiminor_axis = 6356752.314140356; 
//in meters

//Helper functions
function degToRad(deg){return (deg*PI/180)};
function radToDeg(rad){return (rad*180/PI)};
function USsurveyFtToMeters(ft){return ft*linear_unit};

//Convert projection information into radians
var cmRad = degToRad(central_meridian);

//== Ellipsoid definitions ==
//Flattening of the ellipsoid
var f = (semimajor_axis - semiminor_axis) / semimajor_axis;
//Inverse flattening, should be 298.25722210088 or close to it
//var invFlat = 1 / f; //Commented out as no other expressions use it in this script
//First eccentricity squared, should be 0.0066943800229034 or close to it
var ecc_sq = 2*f-Pow(f,2);
//Second eccentricity squared
var secondEcc_sq = ecc_sq/(1-ecc_sq);

//Equations and constants adapted from https://geodesy.noaa.gov/library/pdfs/NOAA_Manual_NOS_NGS_0005.pdf
//REMEMBER TO CHECK IF LONGITUDE/X-COORDINATE FORMULA AND CENTRAL MERIDIAN USES POSITIVE WEST OR POSITIVE EAST!
//https://geodesy.noaa.gov/library/pdfs/Transverse_Mercator_Projection_Tables_New_York.pdf
//Projection constants
var r_s = 6367449.14577;//Radius of the rectifying sphere (GRS 80, meters)
var V_0 = 0.005022893948;
var V_2 = 0.000029370625;
var V_4 = 0.000000235059;
var V_6 = 0.000000002181;

function reverseTM(X,Y){
//Equations taken from and variables based on those used in chapter 3.2, Transverse Mercator Mapping Equations, of NOAA Manual NOS NGS 5, State Plane Coordinate System of 1983, linked above
//Linear units in these equations are the units of false northing/easting and ellipsoid (meters)
//All angles are in radians
var X_m = USsurveyFtToMeters(X);
var Y_m = USsurveyFtToMeters(Y);
var rectifying_latitude = (Y_m - false_northing + meridional_distance_origin)/(scale_factor_origin*r_s);
var footprint_lat = rectifying_latitude + (sin(rectifying_latitude)*cos(rectifying_latitude)) * (V_0 + Pow(cos(rectifying_latitude),2) * (V_2 + Pow(cos(rectifying_latitude),2) * (V_4 + V_6 * Pow(cos(rectifying_latitude),2))));
var footprint_R = scale_factor_origin * semimajor_axis / Pow(1 - ecc_sq * Pow(sin(footprint_lat),2),0.5);
var Q = (X_m - false_easting) / footprint_R;
var t_f = tan(footprint_lat); //tangent of footprint latitude
var Eta2_f = secondEcc_sq*Pow(cos(footprint_lat),2); //lowercase Eta squared, using footprint latitude
var B_2 = -0.5*t_f*(1+Eta2_f);
var B_4 = (-1/12)*(5+(3*Pow(t_f,2))+(Eta2_f*(1-(9*Pow(t_f,2))))-(4*Pow(Eta2_f,2)));
var B_6 = (1/360)*(61+(90*Pow(t_f,2))+(45*Pow(t_f,4))+(Eta2_f*(46-(252*Pow(t_f,2))-(90*Pow(t_f,4)))));
//latitude in radians; positive is north
var latitude_rad = footprint_lat+B_2*Pow(Q,2)*(Pow(Q,2)*(B_6*Pow(Q,2)+B_4)+1);
var B_3 = (-1/6)*(1+(2*Pow(t_f,2))+Eta2_f);
var B_5 = (1/120)*(5+(28*Pow(t_f,2))+(24*Pow(t_f,4))+(Eta2_f*(8*Pow(t_f,2)+6)));
var B_7 = (-1/5040)*(61+(662*Pow(t_f,2))+(1320*Pow(t_f,4))+(720*Pow(t_f,6)));
var L = Q*(Pow(Q,2)*(Pow(Q,2)*(B_7*Pow(Q,2)+B_5)+B_3)+1);
//longitude in radians, positive is east; longitude_rad should use subtraction instead of addition if central_meridian is positive.
var longitude_rad = cmRad+(L/cos(footprint_lat));

var Lat = radToDeg(latitude_rad);
var Lon = radToDeg(longitude_rad);

return [Lat,Lon];
}

// === Get point coordinates from the feature ===
var x = Geometry($feature).x;
var y = Geometry($feature).y;

//Convert to Lat/Lon
var latlon = reverseTM(x,y);

//Output for attribute rules:
return{
        "result":{
             "attributes":{
                   "LAT_CALCULATED": latlon[0],
                   "LON_CALCULATED": latlon[1] //Use the field names for latitude and longitude in the double quotes
                   }
                }
}

 

0 Kudos
AlfredBaldenweck
MVP Regular Contributor

Another use for much-wanted ProjectGeometry()  Idea.

Upvote it here: Arcade projectAs Geometry Function