示例#1
0
        Image GetImage(Operation op, LineFeature line)
        {
            // If the creating op can't be updated, use a no-entry sign.
            if (!(op is IRevisable))
            {
                return(smallImageList.Images["NoEntry"]);
            }

            // If the line is not a circular curve, use the line icon.
            ArcFeature arc = (line as ArcFeature);

            if (arc == null)
            {
                return(smallImageList.Images["Line"]);
            }

            // This leaves us with either a circle, or gray circle.
            if (arc.Circle.Creator == op)
            {
                return(smallImageList.Images["Circle"]);
            }
            else
            {
                return(smallImageList.Images["GrayCircle"]);
            }
        }
示例#2
0
        /// <summary>
        /// Performs data processing that involves creating or retiring spatial features.
        /// Newly created features will not have any definition for their geometry - a
        /// subsequent call to <see cref="CalculateGeometry"/> is needed to to that.
        /// </summary>
        /// <param name="ff">The factory class for generating any spatial features</param>
        internal override void ProcessFeatures(FeatureFactory ff)
        {
            // If the closing point does not already exist, create one at some unspecified position
            OffsetPoint  offset = (m_Radius as OffsetPoint);
            PointFeature p      = (offset == null ? null : offset.Point);

            if (p == null)
            {
                p = ff.CreatePointFeature(DataField.ClosingPoint);
            }

            // Form the construction line (there is no associated circle at this stage, because
            // geometry does not get created until CalculateGeometry)
            ArcFeature arc = ff.CreateArcFeature(DataField.Arc, p, p);

            // Attach a new circle with undefined radius (at this stage, we do NOT cross-reference
            // it to the center point or add it to the map model, since that should be left unti;
            // CalculateGeometry).

            // Note: Setting the arc geometry also updates the circle to refer back to the feature.

            var circle = new Circle(m_Center, 0.0);

            arc.Geometry = new ArcGeometry(circle, arc.StartPoint, arc.StartPoint, true);

            base.SetNewLine(arc);
        }
示例#3
0
        /// <summary>
        /// Performs the data processing associated with this editing operation.
        /// </summary>
        /// <param name="ctx">The context in which the geometry is being calculated.</param>
        internal override void CalculateGeometry(EditingContext ctx)
        {
            // Get the radius, in meters on the ground.
            double rad = m_Radius.GetDistance(m_Center).Meters;

            if (rad < Constants.TINY)
            {
                throw new Exception("NewCircleOperation.CalculateGeometry - Radius is too close to zero.");
            }

            // If the closing point was created by this edit, define it's position
            ArcFeature   arc = (ArcFeature)this.Line;
            PointFeature p   = arc.StartPoint;

            if (p.Creator == this)
            {
                PointGeometry pg = new PointGeometry(m_Center.X, m_Center.Y + rad);
                p.ApplyPointGeometry(ctx, pg);
            }

            // Define the radius of the circle and include in the map model
            Circle circle = arc.Circle;

            Debug.Assert(circle != null);
            circle.Radius = rad;

            // Refer the center point to the circle.
            circle.AddReferences();
        }
示例#4
0
        /// <summary>
        /// Returns the offset for the parallel, in units on the mapping plane. In order
        /// for this to work, a prior call to Calculate must be made.
        /// </summary>
        /// <returns>The offset distance on the mapping plane (>= 0).</returns>
        internal double GetPlanarOffset()
        {
            // If the reference line or the parallel points are undefined, there's nothing we can do.
            if (m_Line == null || m_Par1 == null || m_Par2 == null)
            {
                return(0.0);
            }

            // If the reference line is a curve, get the curve info.
            ArcFeature arc = m_Line.GetArcBase();

            if (arc != null)
            {
                // Get the (planar) radial offset from the circle to one of the parallel positions.
                double    radius = arc.Circle.Radius;
                IPosition center = arc.Circle.Center;
                return(Math.Abs(Geom.Distance(center, m_Par1) - radius));
            }

            // Get the ends of the reference line.
            IPosition spos = m_Line.StartPoint;
            IPosition epos = m_Line.EndPoint;

            // And its bearing.
            double bearing = Geom.BearingInRadians(spos, epos);

            // Get the perpendicular distance (signed) from one of the
            // parallel points to the reference line.
            double offdist = Geom.SignedDistance(spos.X, spos.Y, bearing, m_Par1.X, m_Par1.Y);

            return(Math.Abs(offdist));
        }
示例#5
0
        /// <summary>
        /// Performs the data processing associated with this editing operation.
        /// </summary>
        /// <param name="ctx">The context in which the geometry is being calculated.</param>
        internal override void CalculateGeometry(EditingContext ctx)
        {
            IPosition     p  = Calculate();
            PointGeometry pg = PointGeometry.Create(p);

            m_NewPoint.ApplyPointGeometry(ctx, pg);

            // If the extension line was a circular arc, we also need to define it's geometry.
            // This COULD have been defined at an earlier stage (e.g. as part of CreateFeature),
            // but it's more consistent to do it as part of this method.

            if (m_NewLine is ArcFeature)
            {
                ArcFeature arc    = m_ExtendLine.GetArcBase();
                Circle     circle = arc.Circle;
                Debug.Assert(circle != null);

                bool iscw = arc.IsClockwise;
                if (!m_IsExtendFromEnd)
                {
                    iscw = !iscw;
                }

                ArcGeometry geom = new ArcGeometry(circle, m_NewLine.StartPoint, m_NewLine.EndPoint, iscw);
                (m_NewLine as ArcFeature).Geometry = geom;
            }
        }
示例#6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="NewArcOperation"/> class
        /// using the data read from persistent storage.
        /// </summary>
        /// <param name="editDeserializer">The mechanism for reading back content.</param>
        internal NewArcOperation(EditDeserializer editDeserializer)
            : base(editDeserializer)
        {
            // I originally let the base class do it, but it needs to be an instance
            // of ArcFeature. This is a bit rough - does NewArcOperation really need
            // to extend NewLineOperation?
            ArcFeature arc = editDeserializer.ReadPersistent <ArcFeature>(DataField.Line);

            SetNewLine(arc);
        }
示例#7
0
        /// <summary>
        /// Obtains the features that are referenced by this operation (including features
        /// that are indirectly referenced by observation classes).
        /// </summary>
        /// <returns>
        /// The referenced features (never null, but may be an empty array).
        /// </returns>
        public override Feature[] GetRequiredFeatures()
        {
            List <Feature> result = new List <Feature>(base.GetRequiredFeatures());
            ArcFeature     arc    = (ArcFeature)this.Line;
            PointFeature   center = arc.Circle.CenterPoint;

            if (center.Creator != this)
            {
                result.Add(center);
            }

            return(result.ToArray());
        }
示例#8
0
        /// <summary>
        /// Calculates positions that are parallel to a line.
        /// </summary>
        /// <param name="line">The reference line.</param>
        /// <param name="offpoint">The point the parallel must pass through.</param>
        /// <param name="sres">The position of the start of the parallel.</param>
        /// <param name="eres">The position of the end of the parallel.</param>
        /// <returns>True if positions calculated ok</returns>
        internal static bool Calculate(LineFeature refline, PointFeature offpoint, out IPosition sres, out IPosition eres)
        {
            // No result positions so far.
            sres = eres = null;

            // Get the ends of the reference line.
            IPosition spos = refline.StartPoint;
            IPosition epos = refline.EndPoint;

            // If the reference line is a circular arc
            ArcFeature arc = refline.GetArcBase();

            if (arc != null)
            {
                // Get the curve info
                Circle    circle = arc.Circle;
                double    radius = circle.Radius;
                IPosition centre = circle.Center;
                bool      iscw   = arc.IsClockwise;

                // Get the (planar) distance from the centre of the
                // circle to the offset point.
                double offdist = Geom.Distance(offpoint, centre);

                // Project the BC/EC radially.
                double sbear = Geom.BearingInRadians(centre, spos);
                sres = Geom.Polar(centre, sbear, offdist);

                double ebear = Geom.BearingInRadians(centre, epos);
                eres = Geom.Polar(centre, ebear, offdist);
            }
            else
            {
                double bearing = Geom.BearingInRadians(spos, epos);

                // Get the perpendicular distance (signed) from the offset point
                // to the reference line.
                double offdist = Geom.SignedDistance(spos.X, spos.Y, bearing, offpoint.X, offpoint.Y);

                // Calculate the parallel points.
                bearing += Constants.PIDIV2;
                sres     = Geom.Polar(spos, bearing, offdist);
                eres     = Geom.Polar(epos, bearing, offdist);
            }

            return(true);
        }
示例#9
0
        internal void Draw() // was Paint
        {
            // Nothing to do if parallel points undefined.
            if (m_South == null || m_North == null)
            {
                return;
            }

            Debug.Assert(m_Line != null);
            ISpatialDisplay draw        = m_Cmd.ActiveDisplay;
            IDrawStyle      solidStyle  = EditingController.Current.Style(Color.Magenta);
            IDrawStyle      dottedStyle = new DottedStyle();

            ArcFeature arc = m_Line.GetArcBase();

            if (arc != null)
            {
                // The parallel portion is solid, while the remaining portion of the circle is dotted.
                CircularArcGeometry cg = new CircularArcGeometry(arc.Circle.Center, m_South, m_North, arc.IsClockwise);
                solidStyle.Render(draw, cg);
                cg.IsClockwise = !cg.IsClockwise;
                dottedStyle.Render(draw, cg);
            }
            else
            {
                // What's the bearing from the start to the end of the parallel?
                double bearing = Geom.BearingInRadians(m_South, m_North);

                // What's the max length of a diagonal crossing the entire screen?
                double maxdiag = m_Cmd.MaxDiagonal;

                // Project to a point below the southern end of the parallel, as
                // well as a point above the northern end.
                IPosition below = Geom.Polar(m_South, bearing + Constants.PI, maxdiag);
                IPosition above = Geom.Polar(m_North, bearing, maxdiag);

                LineSegmentGeometry.Render(below, m_South, draw, dottedStyle);
                LineSegmentGeometry.Render(m_South, m_North, draw, solidStyle);
                LineSegmentGeometry.Render(m_North, above, draw, dottedStyle);

                // If we have an offset point, draw it in green.
                if (m_Point != null)
                {
                    m_Point.Draw(draw, Color.Green);
                }
            }
        }
示例#10
0
        /// <summary>
        /// Performs the data processing associated with this editing operation.
        /// </summary>
        /// <param name="ctx">The context in which the geometry is being calculated.</param>
        internal override void CalculateGeometry(EditingContext ctx)
        {
            // Calculate the end positions
            IPosition spos, epos;

            if (!Calculate(out spos, out epos))
            {
                throw new Exception("Failed to calculate parallel line positions");
            }

            // Apply the calculated positions so long as the end points of the parallel line
            // were created by this edit
            if (m_ParLine.StartPoint.Creator == this)
            {
                m_ParLine.StartPoint.ApplyPointGeometry(ctx, PointGeometry.Create(spos));
            }

            if (m_ParLine.EndPoint.Creator == this)
            {
                m_ParLine.EndPoint.ApplyPointGeometry(ctx, PointGeometry.Create(epos));
            }

            // If the parallel is an arc, define the geometry
            if (m_ParLine is ArcFeature)
            {
                // Get the center of the reference line
                ArcFeature   refArc = m_RefLine.GetArcBase();
                PointFeature center = refArc.Circle.CenterPoint;

                // Obtain a circle for the parallel
                double radius = Geom.Distance(center, m_ParLine.StartPoint);
                Circle circle = MapModel.AddCircle(center, radius);

                // Define arc direction
                bool iscw = refArc.IsClockwise;
                if (IsArcReversed)
                {
                    iscw = !iscw;
                }

                ArcGeometry geom = new ArcGeometry(circle, m_ParLine.StartPoint, m_ParLine.EndPoint, iscw);
                (m_ParLine as ArcFeature).Geometry = geom;
            }
        }
示例#11
0
        /// <summary>
        /// Creates any new spatial features (without any geometry)
        /// </summary>
        /// <param name="ff">The factory class for generating spatial features</param>
        internal override void ProcessFeatures(FeatureFactory ff)
        {
            m_NewPoint = ff.CreatePointFeature(DataField.NewPoint);

            if (ff.HasFeatureDescription(DataField.NewLine))
            {
                PointFeature from = (m_IsExtendFromEnd ? m_ExtendLine.EndPoint : m_ExtendLine.StartPoint);
                ArcFeature   arc  = m_ExtendLine.GetArcBase();

                if (arc == null)
                {
                    m_NewLine = ff.CreateSegmentLineFeature(DataField.NewLine, from, m_NewPoint);
                }
                else
                {
                    m_NewLine = ff.CreateArcFeature(DataField.NewLine, from, m_NewPoint);
                }

                m_NewLine.ObservedLength = m_Length;
            }
        }
示例#12
0
        /// <summary>
        /// Returns the intersection of the parallel with a line. A prior call to Calculate is required.
        /// </summary>
        /// <param name="line">The line to intersect with.</param>
        /// <param name="isEndParallel">Is the intersection biased towards the end of the parallel?</param>
        /// <returns>The intersection (if any). In cases where the line intersects the parallel
        /// more than once, you get an arbitrary intersection.</returns>
        internal IPosition GetIntersect(LineFeature line, bool isEndParallel)
        {
            // Make sure the intersection is undefined.
            IPosition result = null;

            // Return if the parallel points are undefined.
            if (m_Par1 == null || m_Par2 == null)
            {
                return(null);
            }

            // If the reference line is a circular arc, get the curve info.
            ArcFeature arc = m_Line.GetArcBase();

            if (arc != null)
            {
                Circle         circle = arc.Circle;
                double         radius = circle.Radius;
                IPointGeometry centre = circle.Center;
                bool           iscw   = arc.IsClockwise;

                // Construct a circle that passes through
                // the parallel points (assumed to have the same distance
                // with respect to the centre of the circle).
                double parrad = Geom.Distance(centre, m_Par1);

                // Intersect the circle with the line to intersect with.
                IntersectionResult xres = new IntersectionResult(line);
                uint nx = xres.Intersect(centre, parrad);
                if (nx == 0)
                {
                    return(null);
                }

                // If there is only one intersection, that's what we want.
                if (nx == 1)
                {
                    return(xres.Intersections[0].P1);
                }

                // Get the intersection that is closest to the parallel point
                // that has the bias.
                if (isEndParallel)
                {
                    xres.GetClosest(m_Par2, out result, 0.0);
                }
                else
                {
                    xres.GetClosest(m_Par1, out result, 0.0);
                }
            }
            else
            {
                // Get the bearing from the start to the end of the parallel.
                double bearing = Geom.BearingInRadians(m_Par1, m_Par2);

                // Get the ground dimension of a line that crosses the
                // extent of the draw window.
                double dist = MaxDiagonal;

                // Project the parallel line to positions that are well
                // beyond the draw extent.
                IPosition start = Geom.Polar(m_Par1, bearing + Constants.PI, dist);
                IPosition end   = Geom.Polar(m_Par2, bearing, dist);

                // Intersect the line segment with the line to intersect with.
                IntersectionResult xres = new IntersectionResult(line);
                IPointGeometry     sg   = PointGeometry.Create(start);
                IPointGeometry     eg   = PointGeometry.Create(end);
                uint nx = xres.Intersect(sg, eg);
                if (nx == 0)
                {
                    return(null);
                }

                // If there is only one intersection, that's what we want.
                if (nx == 1)
                {
                    return(xres.Intersections[0].P1);
                }

                // Get the intersection that is closest to the parallel point
                // that has the bias.
                if (isEndParallel)
                {
                    xres.GetClosest(m_Par2, out result, 0.0);
                }
                else
                {
                    xres.GetClosest(m_Par1, out result, 0.0);
                }
            }

            return(result);
        }
示例#13
0
        /// <summary>
        /// Calculates positions that are parallel to a line.
        /// </summary>
        /// <param name="line">The reference line.</param>
        /// <param name="offset">The offset to the parallel, in ground units. Signed to denote
        /// which side (less than zero means it's to the left of the reference line).</param>
        /// <param name="sres">The position of the start of the parallel.</param>
        /// <param name="eres">The position of the end of the parallel.</param>
        /// <returns>True if positions calculated ok</returns>
        static bool Calculate(LineFeature line, Distance offset, out IPosition sres, out IPosition eres)
        {
            // No result positions so far.
            sres = eres = null;

            // Get the ends of the reference line.
            IPosition spos = line.StartPoint;
            IPosition epos = line.EndPoint;

            ISpatialSystem sys = CadastralMapModel.Current.SpatialSystem;

            // If the reference line is a circular arc, get the curve info.
            ArcFeature arc = line.GetArcBase();

            if (arc != null)
            {
                Circle    circle = arc.Circle;
                double    radius = circle.Radius;
                IPosition centre = circle.Center;
                bool      iscw   = arc.IsClockwise;

                // Get the midpoint of the curve. The reduction of the
                // ground distance will be along the line that goes
                // from the centre of the circle & through this position.

                ILength   len     = line.Length;
                ILength   halfLen = new Length(len.Meters * 0.5);
                IPosition middle;
                line.LineGeometry.GetPosition(halfLen, out middle);

                // Get the bearing from the centre to the mid-position
                // and use that to reduce the offset to the mapping plane.
                double bearing = Geom.BearingInRadians(centre, middle);
                double offdist = offset.GetPlanarMetric(middle, bearing, sys);

                // No parallel if the offset exceeds the radius.
                // if ( offdist > radius ) return FALSE;

                // Calculate the parallel points.
                double sbear = Geom.BearingInRadians(centre, spos);
                sres = Geom.Polar(centre, sbear, offdist + radius);

                double ebear = Geom.BearingInRadians(centre, epos);
                eres = Geom.Polar(centre, ebear, offdist + radius);
            }
            else
            {
                // Get the bearing.of the line.
                double bearing = Geom.BearingInRadians(spos, epos);

                // Get the planar distance for a perpendicular line that passes
                // through the midpoint of the reference line. The planar distance
                // will have the same sign as the ground value.

                IPosition middle = Position.CreateMidpoint(spos, epos);
                bearing += Constants.PIDIV2;
                double offdist = offset.GetPlanarMetric(middle, bearing, sys);

                // Calculate the parallel points.
                sres = Geom.Polar(spos, bearing, offdist);
                eres = Geom.Polar(epos, bearing, offdist);
            }

            return(true);
        }
示例#14
0
        /// <summary>
        /// Draws the current state of the edit
        /// </summary>
        internal void Draw()
        {
            Debug.Assert(m_Line != null);
            ISpatialDisplay view = ActiveDisplay;

            // Figure out the positions for the ends of the parallel line (if any) ...

            // Assume we already know both terminals.
            IPosition start = m_Term1;
            IPosition end   = m_Term2;

            // If either one is undefined, but a dialog for it is active,
            // try to get the terminal from there instead.
            if (m_TermDial1 != null && start == null)
            {
                start = m_TermDial1.TerminalPosition;
            }

            if (m_TermDial2 != null && end == null)
            {
                end = m_TermDial2.TerminalPosition;
            }

            // If they weren't actually defined, use the parallel points instead.
            if (start == null)
            {
                start = m_Par1;
            }

            if (end == null)
            {
                end = m_Par2;
            }

            // If those weren't defined either, try to calculate them now.
            if (end == null && Calculate())
            {
                start = m_Par1;
                end   = m_Par2;
            }

            // Any offset point
            if (m_OffsetPoint != null)
            {
                m_OffsetPoint.Draw(view, Color.Green);
            }

            // Everything else should draw in usual command-style colour.
            IDrawStyle style       = EditingController.Current.Style(Color.Magenta);
            IDrawStyle dottedStyle = new DottedStyle();

            // If the reference line is a curve, get the curve info.
            ArcFeature arc = m_Line.GetArcBase();

            if (arc != null)
            {
                bool iscw = arc.IsClockwise;

                // Reverse the direction if necessary.
                if (m_IsReversed)
                {
                    iscw = !iscw;
                }

                // Draw the parallel line (the rest of the circle being dotted).
                if (start != null)
                {
                    CircularArcGeometry parArc = new CircularArcGeometry(arc.Circle, start, end, iscw);
                    style.Render(view, parArc);

                    parArc.IsClockwise = !parArc.IsClockwise;
                    dottedStyle.Render(view, parArc);
                }
            }
            else
            {
                // PARALLEL IS STRAIGHT

                // If we've got something, figure out positions for dotted portion.
                if (start != null)
                {
                    // What's the max length of a diagonal crossing the entire screen?
                    double maxdiag = this.MaxDiagonal;

                    // What's the bearing from the start to the end of the parallel?
                    double bearing = Geom.BearingInRadians(start, end);

                    // Project to a point before the start end of the parallel, as
                    // well as a point after the end.
                    IPosition before = Geom.Polar(start, bearing + Constants.PI, maxdiag);
                    IPosition after  = Geom.Polar(end, bearing, maxdiag);

                    LineSegmentGeometry.Render(before, start, view, dottedStyle);
                    LineSegmentGeometry.Render(start, end, view, style);
                    LineSegmentGeometry.Render(end, after, view, dottedStyle);
                }
            }

            // Draw terminal positions (if defined).

            if (m_Term1 != null)
            {
                style.Render(view, m_Term1);
            }

            if (m_Term2 != null)
            {
                style.Render(view, m_Term2);
            }

            // The terminal lines.

            if (m_TermLine1 != null)
            {
                m_TermLine1.Render(view, style);
            }

            if (m_TermLine2 != null)
            {
                m_TermLine2.Render(view, style);
            }

            // Do the active dialog last so their stuff draws on top.
            if (m_ParDial != null)
            {
                m_ParDial.Draw();
            }

            if (m_TermDial1 != null)
            {
                m_TermDial1.Draw();
            }

            if (m_TermDial2 != null)
            {
                m_TermDial2.Draw();
            }
        }
示例#15
0
        /// <summary>
        /// Returns the intersection of the parallel with a line.
        /// </summary>
        /// <param name="refline">The reference line.</param>
        /// <param name="parpos">Search position that coincides with the parallel.</param>
        /// <param name="line">The line to intersect with.</param>
        /// <returns>The intersection (if any). In cases where the line intersects the
        /// parallel more than once, you get the intersection that is closest to the
        /// search position.</returns>
        internal static IPosition GetIntersect(LineFeature refline, IPosition parpos, LineFeature line)
        {
            // Make sure the intersection is undefined.
            IPosition result = null;

            // Return if the parallel point is undefined.
            if (parpos == null)
            {
                return(null);
            }

            // If the reference line is a circular arc (or a section based on an arc), get the curve info.
            ArcFeature arc = refline.GetArcBase();

            if (arc != null)
            {
                Circle         circle = arc.Circle;
                double         radius = circle.Radius;
                IPointGeometry centre = circle.Center;
                bool           iscw   = arc.IsClockwise;

                // Construct a circle that passes through the search position
                double parrad = Geom.Distance(centre, parpos);

                // Intersect the circle with the line to intersect with.
                IntersectionResult xres = new IntersectionResult(line);
                uint nx = xres.Intersect(centre, parrad);
                if (nx == 0)
                {
                    return(null);
                }

                // If there is only one intersection, that's what we want.
                if (nx == 1)
                {
                    return(xres.Intersections[0].P1);
                }

                // Get the intersection that is closest to the search position.
                xres.GetClosest(parpos, out result, 0.0);
            }
            else
            {
                // Get the bearing from the start to the end of the reference line.
                IPosition spos    = refline.StartPoint;
                IPosition epos    = refline.EndPoint;
                double    bearing = Geom.BearingInRadians(spos, epos);

                // Project the parallel line to positions that are a long way away (but make sure we
                // don't end up with negative numbers).
                Window searchWindow = new Window(line.Extent);
                searchWindow.Union(refline.Extent);
                searchWindow.Union(parpos);
                double dist = Geom.Distance(searchWindow.Min, searchWindow.Max);

                IPosition start = Geom.Polar(parpos, bearing + Constants.PI, dist);
                IPosition end   = Geom.Polar(parpos, bearing, dist);

                // Intersect the line segment with the line to intersect with.
                IntersectionResult xres = new IntersectionResult(line);
                IPointGeometry     sg   = new PointGeometry(start);
                IPointGeometry     eg   = new PointGeometry(end);
                uint nx = xres.Intersect(sg, eg);
                if (nx == 0)
                {
                    return(null);
                }

                // If there is only one intersection, that's what we want.
                if (nx == 1)
                {
                    return(xres.Intersections[0].P1);
                }

                // Get the intersection that is closest to the search position
                xres.GetClosest(parpos, out result, 0.0);
            }

            return(result);
        }
示例#16
0
        /// <summary>
        /// Calculates the start and end positions of an extension to a circular arc.
        /// </summary>
        /// <param name="extendLine">The line being extended.</param>
        /// <param name="isFromEnd">True if extending from the end of the line.</param>
        /// <param name="dist">The length of the extension.</param>
        /// <param name="start">The position of the start of the extension.</param>
        /// <param name="end">The position of the end of the extension.</param>
        /// <param name="center">The center of the circle on which the arc lies.</param>
        /// <param name="iscw">Is the circular arc directed clockwise?</param>
        /// <returns>True if position have been worked out. False if there is insufficient data,
        /// or the extension is not on a circular arc, or the length is more than the circumference
        /// of the circle (in those cases, the start and end positions come back as nulls)</returns>
        internal static bool Calculate(LineFeature extendLine, bool isFromEnd, Distance dist,
                                       out IPosition start, out IPosition end, out IPosition center, out bool iscw)
        {
            start  = end = null;
            center = null;
            iscw   = true;

            // Can't calculate if there is insufficient data.
            if (extendLine == null || dist == null)
            {
                return(false);
            }

            // The length must be defined.
            if (!dist.IsDefined)
            {
                return(false);
            }

            // The line that's being extended must be a circular arc.
            ArcFeature arc = (extendLine as ArcFeature);

            if (arc == null)
            {
                return(false);
            }

            center = arc.Circle.Center;
            double radius = arc.Circle.Radius;

            iscw = arc.IsClockwise;

            // Get the length of the arc extension, in meters on the ground.
            double arclen = dist.Meters;

            // If the arc length exceeds the length of the circumference,
            // the end point can't be calculated.
            double circumf = Constants.PIMUL2 * radius;

            if (arclen > circumf)
            {
                return(false);
            }

            // If we're extending from the start of the arc, the curve direction has
            // to be reversed too.
            if (!isFromEnd)
            {
                iscw = !iscw;
            }

            // Get the point we're extending from.
            start = (isFromEnd ? extendLine.EndPoint : extendLine.StartPoint);

            // Get the point we're extending to ...

            // Get the bearing from the center of the circle to the start of the arc.
            Turn   turn     = new Turn(center, start);
            double sbearing = turn.BearingInRadians;

            // Get the sector angle (in radians).
            double sector = arclen / radius;

            double ebearing = sbearing;

            if (iscw)
            {
                ebearing += sector;
            }
            else
            {
                ebearing -= sector;
            }

            end = Geom.Polar(center, ebearing, radius);

            // Re-calculate the arc length on the mapping plane,
            arclen = dist.GetPlanarMetric(start, end, extendLine.SpatialSystem);

            // And adjust the end position accordingly.
            sector = arclen / radius;

            if (iscw)
            {
                ebearing = sbearing + sector;
            }
            else
            {
                ebearing = sbearing - sector;
            }

            end = Geom.Polar(center, ebearing, radius);
            return(true);
        }