Programmatically update COGO attributes after CopyLineFeaturesToParcelType edit operation

1918
6
Jump to solution
02-23-2021 11:31 AM
Labels (1)
PascalVezina
Occasional Contributor II

How do you trigger the "Update COGO Attributes" programmatically immediately after the edit operation CopyLineFeaturesToParcelType?

I've tried this code, see attachement "Sample.txt"

 

In my case, the CanExecute is never true in both cases...

Note that the GridToGroundCorrection is ON, and both the Scale Factor and Offset direction are set, and my feature class is COGO Enabled.

Thank you,

 

 

Tags (2)
1 Solution

Accepted Solutions
TimHodson
Esri Contributor

hi Pascal,

Here are the functions. Note there is a slight signature change with addition of the spatial reference parameter. I edited, in place, the calling code in the previous comment:

 

 

    private bool GetCOGOFromGeometry(Polyline myLineFeature, SpatialReference MapSR, double ScaleFactor, double DirectionOffset, out object[] COGODirectionDistanceRadiusArcLength)
    {
      COGODirectionDistanceRadiusArcLength = new object[4] { DBNull.Value,DBNull.Value,DBNull.Value, DBNull.Value };
      try
      {
        COGODirectionDistanceRadiusArcLength[0] = DBNull.Value;
        COGODirectionDistanceRadiusArcLength[1] = DBNull.Value;

        var GeomSR= myLineFeature.SpatialReference;
        if (GeomSR.IsGeographic && MapSR.IsGeographic)
          return false; //no SDK support for Geodesics till 2.8

        double UnitConversion = 1;

        if (GeomSR.IsGeographic && MapSR.IsProjected)
        { //only need to project if dataset is in a GCS.
          UnitConversion = MapSR.Unit.ConversionFactor; // Meters per unit. Only need this for converting to metric for GCS datasets.
          myLineFeature = GeometryEngine.Instance.Project(myLineFeature, MapSR) as Polyline;
        }

        EllipticArcSegment pCircArc;
        ICollection<Segment> LineSegments = new List<Segment>();
        myLineFeature.GetAllSegments(ref LineSegments);
        int numSegments = LineSegments.Count;

        IList<Segment> iList = LineSegments as IList<Segment>;
        Segment FirstSeg = iList[0];
        Segment LastSeg = iList[numSegments - 1];
        var pLine = LineBuilder.CreateLineSegment(FirstSeg.StartCoordinate, LastSeg.EndCoordinate);
        COGODirectionDistanceRadiusArcLength[0] =
PolarRadiansToNorthAzimuthDecimalDegrees(pLine.Angle - DirectionOffset*Math.PI/180);
        COGODirectionDistanceRadiusArcLength[1] = pLine.Length * UnitConversion / ScaleFactor;
        //check if the last segment is a circular arc
        var pCircArcLast = LastSeg as EllipticArcSegment;
        if (pCircArcLast == null)
          return true; //we already know there is no circluar arc COGO
                       //Keep a copy of the center point
        var LastCenterPoint = pCircArcLast.CenterPoint;
        COGODirectionDistanceRadiusArcLength[2] = pCircArcLast.IsCounterClockwise ?
                -pCircArcLast.SemiMajorAxis : Math.Abs(pCircArcLast.SemiMajorAxis); //radius
        double dArcLengthSUM = 0;
        //use 30 times xy tolerance for circular arc segment tangency test
        double dTangencyToleranceTest = MapSR.XYTolerance * 30; //around 3cms if using default XY Tolerance - recommended

        for (int i = 0; i < numSegments; i++)
        {
          pCircArc = iList[i] as EllipticArcSegment;
          if (pCircArc == null)
          {
            COGODirectionDistanceRadiusArcLength[2] = DBNull.Value; //radius
            COGODirectionDistanceRadiusArcLength[3] = DBNull.Value; //arc length
            return true;
          }
          var tolerance = LineBuilder.CreateLineSegment(LastCenterPoint, pCircArc.CenterPoint).Length;
          if (tolerance > dTangencyToleranceTest)
          {
            COGODirectionDistanceRadiusArcLength[2] = DBNull.Value; //radius
            COGODirectionDistanceRadiusArcLength[3] = DBNull.Value; //arc length
            return true;
          }
          dArcLengthSUM += pCircArc.Length; //arc length sum
        }
        //now check to see if the radius and arclength survived and if so, clear the distance
        if (COGODirectionDistanceRadiusArcLength[2] != DBNull.Value)
          COGODirectionDistanceRadiusArcLength[1] = DBNull.Value;

        COGODirectionDistanceRadiusArcLength[3] = dArcLengthSUM * UnitConversion / ScaleFactor;
        COGODirectionDistanceRadiusArcLength[2] = (double)COGODirectionDistanceRadiusArcLength[2] * UnitConversion / ScaleFactor;
        
        return true;
      }
      catch
      {
        return false;
      }
    }

 

 

 

Here is the second function for direction unit/format conversions from polar to north azimuth:

 

 

    private static double PolarRadiansToNorthAzimuthDecimalDegrees(double InPolarRadians)
    {
      var AngConv = DirectionUnitFormatConversion.Instance;
      var ConvDef = new ConversionDefinition()
      {
        DirectionTypeIn = ArcGIS.Core.SystemCore.DirectionType.Polar,
        DirectionUnitsIn = ArcGIS.Core.SystemCore.DirectionUnits.Radians,
        DirectionTypeOut = ArcGIS.Core.SystemCore.DirectionType.NorthAzimuth,
        DirectionUnitsOut = ArcGIS.Core.SystemCore.DirectionUnits.DecimalDegrees
      };
      return AngConv.ConvertToDouble(InPolarRadians, ConvDef);
    }

 

 

 

View solution in original post

6 Replies
TimHodson
Esri Contributor

Hi Pascal,

Below is a snippet of code to programmatically assign the COGO values. The function called GetCOGOFromGeometry is not yet complete, but this should help to get you started on the right track.

Note, all of this and more will be covered in the Dev Summit during the session called: ArcGIS Pro SDK for .NET: Introduction to the Parcel Fabric API, on 8th April.

As you are probably aware, there is also SDK content here. I just mention it for the benefit of others reading this post.

Once it's ready I will also share the code for the function.

-Tim

 

          
          ParcelEditToken peToken = editOper.CopyLineFeaturesToParcelType(srcFeatLyr, ids, 
            destLineL, destPolygonL);

          if(!editOper.Execute())
            return editOper.ErrorMessage;

          //collect ground to grid correction values
          var mapView = MapView.Active;
          if (mapView?.Map == null)
            return "";
          var pSpatRef = mapView.Map.SpatialReference;
          var cimDefinition = mapView.Map?.GetDefinition();
          if (cimDefinition == null) return "";
          var cimG2G = cimDefinition.GroundToGridCorrection;

          double dScaleFactor = cimG2G.GetConstantScaleFactor();
          double dDirectionOffsetCorrection = cimG2G.GetDirectionOffset();

          var editOper2 = editOper.CreateChainedOperation();
          var FeatSetCreated = peToken.CreatedFeatures;

          Dictionary<string, object> ParcelLineAttributes = new Dictionary<string, object>();

          if (FeatSetCreated != null)
          {
            foreach (KeyValuePair<MapMember, List<long>> kvp in FeatSetCreated)
            {
              if (kvp.Key == destPolygonL)
                continue; //skip polygons, we just want to work with the lines
              foreach (long oid in kvp.Value)
              {
                var insp = kvp.Key.Inspect(oid);
                Polyline lineGeom = (Polyline)insp["SHAPE"];

                object[] COGODirectionDistanceRadiusArcLength;

                if (!GetCOGOFromGeometry(lineGeom, pSpatRef, dScaleFactor, dDirectionOffsetCorrection, out COGODirectionDistanceRadiusArcLength))
                { 
                  editOper2.Abort();
                  return ""; 
                }
                ParcelLineAttributes.Add("Direction", COGODirectionDistanceRadiusArcLength[0]);
                ParcelLineAttributes.Add("Distance", COGODirectionDistanceRadiusArcLength[1]);
                ParcelLineAttributes.Add("Radius", COGODirectionDistanceRadiusArcLength[2]);
                ParcelLineAttributes.Add("ArcLength", COGODirectionDistanceRadiusArcLength[3]);
                ParcelLineAttributes.Add("Rotation", dDirectionOffsetCorrection);
                ParcelLineAttributes.Add("Scale", dScaleFactor);
                ParcelLineAttributes.Add("IsCOGOGround", 1);

                editOper2.Modify(kvp.Key, oid, ParcelLineAttributes);
                ParcelLineAttributes.Clear();
              }
              editOper2.Execute();
            }
          }

 

 

 

 

 

 

 

 

 

PascalVezina
Occasional Contributor II

Good day Tim,
Thank you for the reply.
This function GetCOGOFromGeometry will be available in the next API release 2.7.2 or 2.8?
Regards,
Pascal

0 Kudos
TimHodson
Esri Contributor

Pascal, the function will be shared either today, tomorrow, or early next week. It is written, but needs some testing. Note that you have access to the full geometry API, and the lines in the parcel fabric are standard line features. For example, below is a code snippet to return the info for the straight line between the first and last vertex of the feature.

-Tim

 

ICollection<Segment> LineSegments = new List<Segment>();
myLineFeature.GetAllSegments(ref LineSegments);
int numSegments = LineSegments.Count;

IList<Segment> iList = LineSegments as IList<Segment>;
Segment FirstSeg = iList[0];
Segment LastSeg = iList[numSegments - 1];
var pLine = LineBuilder.CreateLineSegment(FirstSeg.StartCoordinate, LastSeg.EndCoordinate);
var dDirectionPolarRadians = pLine.Angle;
var dDistance = pLine.Length;

 

0 Kudos
TimHodson
Esri Contributor

hi Pascal,

Here are the functions. Note there is a slight signature change with addition of the spatial reference parameter. I edited, in place, the calling code in the previous comment:

 

 

    private bool GetCOGOFromGeometry(Polyline myLineFeature, SpatialReference MapSR, double ScaleFactor, double DirectionOffset, out object[] COGODirectionDistanceRadiusArcLength)
    {
      COGODirectionDistanceRadiusArcLength = new object[4] { DBNull.Value,DBNull.Value,DBNull.Value, DBNull.Value };
      try
      {
        COGODirectionDistanceRadiusArcLength[0] = DBNull.Value;
        COGODirectionDistanceRadiusArcLength[1] = DBNull.Value;

        var GeomSR= myLineFeature.SpatialReference;
        if (GeomSR.IsGeographic && MapSR.IsGeographic)
          return false; //no SDK support for Geodesics till 2.8

        double UnitConversion = 1;

        if (GeomSR.IsGeographic && MapSR.IsProjected)
        { //only need to project if dataset is in a GCS.
          UnitConversion = MapSR.Unit.ConversionFactor; // Meters per unit. Only need this for converting to metric for GCS datasets.
          myLineFeature = GeometryEngine.Instance.Project(myLineFeature, MapSR) as Polyline;
        }

        EllipticArcSegment pCircArc;
        ICollection<Segment> LineSegments = new List<Segment>();
        myLineFeature.GetAllSegments(ref LineSegments);
        int numSegments = LineSegments.Count;

        IList<Segment> iList = LineSegments as IList<Segment>;
        Segment FirstSeg = iList[0];
        Segment LastSeg = iList[numSegments - 1];
        var pLine = LineBuilder.CreateLineSegment(FirstSeg.StartCoordinate, LastSeg.EndCoordinate);
        COGODirectionDistanceRadiusArcLength[0] =
PolarRadiansToNorthAzimuthDecimalDegrees(pLine.Angle - DirectionOffset*Math.PI/180);
        COGODirectionDistanceRadiusArcLength[1] = pLine.Length * UnitConversion / ScaleFactor;
        //check if the last segment is a circular arc
        var pCircArcLast = LastSeg as EllipticArcSegment;
        if (pCircArcLast == null)
          return true; //we already know there is no circluar arc COGO
                       //Keep a copy of the center point
        var LastCenterPoint = pCircArcLast.CenterPoint;
        COGODirectionDistanceRadiusArcLength[2] = pCircArcLast.IsCounterClockwise ?
                -pCircArcLast.SemiMajorAxis : Math.Abs(pCircArcLast.SemiMajorAxis); //radius
        double dArcLengthSUM = 0;
        //use 30 times xy tolerance for circular arc segment tangency test
        double dTangencyToleranceTest = MapSR.XYTolerance * 30; //around 3cms if using default XY Tolerance - recommended

        for (int i = 0; i < numSegments; i++)
        {
          pCircArc = iList[i] as EllipticArcSegment;
          if (pCircArc == null)
          {
            COGODirectionDistanceRadiusArcLength[2] = DBNull.Value; //radius
            COGODirectionDistanceRadiusArcLength[3] = DBNull.Value; //arc length
            return true;
          }
          var tolerance = LineBuilder.CreateLineSegment(LastCenterPoint, pCircArc.CenterPoint).Length;
          if (tolerance > dTangencyToleranceTest)
          {
            COGODirectionDistanceRadiusArcLength[2] = DBNull.Value; //radius
            COGODirectionDistanceRadiusArcLength[3] = DBNull.Value; //arc length
            return true;
          }
          dArcLengthSUM += pCircArc.Length; //arc length sum
        }
        //now check to see if the radius and arclength survived and if so, clear the distance
        if (COGODirectionDistanceRadiusArcLength[2] != DBNull.Value)
          COGODirectionDistanceRadiusArcLength[1] = DBNull.Value;

        COGODirectionDistanceRadiusArcLength[3] = dArcLengthSUM * UnitConversion / ScaleFactor;
        COGODirectionDistanceRadiusArcLength[2] = (double)COGODirectionDistanceRadiusArcLength[2] * UnitConversion / ScaleFactor;
        
        return true;
      }
      catch
      {
        return false;
      }
    }

 

 

 

Here is the second function for direction unit/format conversions from polar to north azimuth:

 

 

    private static double PolarRadiansToNorthAzimuthDecimalDegrees(double InPolarRadians)
    {
      var AngConv = DirectionUnitFormatConversion.Instance;
      var ConvDef = new ConversionDefinition()
      {
        DirectionTypeIn = ArcGIS.Core.SystemCore.DirectionType.Polar,
        DirectionUnitsIn = ArcGIS.Core.SystemCore.DirectionUnits.Radians,
        DirectionTypeOut = ArcGIS.Core.SystemCore.DirectionType.NorthAzimuth,
        DirectionUnitsOut = ArcGIS.Core.SystemCore.DirectionUnits.DecimalDegrees
      };
      return AngConv.ConvertToDouble(InPolarRadians, ConvDef);
    }

 

 

 

TimHodson
Esri Contributor

With 2.9 we now have new SDK documentation content on COGO API topics:

https://github.com/esri/arcgis-pro-sdk/wiki/ProConcepts-COGO

0 Kudos
TimHodson
Esri Contributor

For the latest code for Update COGO, including access and use of the map’s ground to grid corrections, see the Parcel Utilities Add-in.

Install: https://arcg.is/0f5reS

Code: https://github.com/Esri/parcel-fabric-pro-addins/tree/main/ParcelsAddin

TimHodson_0-1708998074233.png

The configuration settings are designed to protect against the overwriting of good COGO:

TimHodson_1-1708998074235.png

These settings are saved between Pro sessions.

-Tim

0 Kudos