// If the specified position isn't actually on the arc, the length is to the // position when it's projected onto the arc (i.e. the perpendicular position) public static ILength GetLength(ICircularArcGeometry g, IPosition asFarAs) { ICircleGeometry circle = g.Circle; double radius = circle.Radius; if (asFarAs == null) { // If the BC coincides with the EC, it's possible the arc has zero // length. As a matter of convention, counter-clockwise arcs will // be regarded as having a length of zero in that case. Meanwhile, // clockwise arcs will have a length that corresponds to the complete // circumference of the circle. if (g.BC.IsCoincident(g.EC)) { if (g.IsClockwise) { return(CircleGeometry.GetLength(circle)); } else { return(Backsight.Length.Zero); } } return(new Length(radius * g.SweepAngleInRadians)); } // Express the position of the BC in a local coordinate system. IPosition c = circle.Center; QuadVertex bc = new QuadVertex(c, g.BC, radius); // Calculate the clockwise angle to the desired point. QuadVertex to = new QuadVertex(c, asFarAs, radius); double ang = to.BearingInRadians - bc.BearingInRadians; if (ang < 0.0) { ang += MathConstants.PIMUL2; } // If the curve is actually anti-clockwise, take the complement. if (!g.IsClockwise) { ang = MathConstants.PIMUL2 - ang; } return(new Length(radius * ang)); }
/// <summary> /// The distance from the specified position to the perimeter of this circle. /// </summary> /// <param name="p">The position of interest</param> /// <returns>The distance from the specified position to the perimeter of /// this circle.</returns> public ILength Distance(IPosition p) { return(CircleGeometry.Distance(this, p)); }
public void Render(ISpatialDisplay display, IDrawStyle style) { CircleGeometry.Render(this, display, style); }
// Circle internal uint Intersect(IPointGeometry center, double radius) { ICircleGeometry circle = new CircleGeometry(center, radius); return m_IntersectedObject.LineGeometry.IntersectCircle(this, circle); }
private ArcFeature ImportArc(Ntx.Line line, Operation creator, ILength tol) { Debug.Assert(line.IsCurve); IEntity what = GetEntityType(line, SpatialType.Line); // Get positions defining the arc PointGeometry[] pts = GetPositions(line); // Ignore zero-length lines if (HasZeroLength(pts)) return null; // Add a point at the center of the circle Ntx.Position pos = line.Center; PointGeometry pc = new PointGeometry(pos.Easting, pos.Northing); PointFeature center = EnsurePointExists(pc, tol, creator); // Calculate exact positions for the arc endpoints double radius = line.Radius; ICircleGeometry cg = new CircleGeometry(pc, radius); IPosition bc = CircleGeometry.GetClosestPosition(cg, pts[0]); IPosition ec = CircleGeometry.GetClosestPosition(cg, pts[pts.Length-1]); // Round off to nearest micron PointGeometry bcg = PointGeometry.Create(bc); PointGeometry ecg = PointGeometry.Create(ec); // Ensure point features exist at both ends of the line. PointFeature ps = GetArcEndPoint(bcg, tol, creator); PointFeature pe = GetArcEndPoint(ecg, tol, creator); // Try to find a circle that's already been added by this import. Circle c = EnsureCircleExists(center, radius, tol, creator); // Determine which way the arc is directed bool iscw = LineStringGeometry.IsClockwise(pts, center); InternalIdValue id = CadastralMapModel.Current.WorkingSession.AllocateNextId(); ArcFeature arc = new ArcFeature(creator, id, what, c, ps, pe, iscw); // The toological status of the incoming arc may override the status that the // constructor derived from the entity type arc.SetTopology(line.IsTopologicalArc); #if DEBUG // Confirm the NTX data was valid (ensure it's consistent with what we've imported)... double readRad = c.Radius; double calcRad = BasicGeom.Distance(c.Center, ps); Debug.Assert(Math.Abs(readRad-calcRad) < tol.Meters); foreach (IPointGeometry pg in pts) { ILength check = arc.Geometry.Distance(pg); Debug.Assert(check.Meters < tol.Meters); } #endif return arc; }
public static IWindow GetExtent(ICircularArcGeometry g) { // If the curve is a complete circle, just define it the easy way. if (g.BC.IsCoincident(g.EC)) { return(CircleGeometry.GetExtent(g.Circle)); } IPosition bcp = g.BC; IPosition ecp = g.EC; IPosition centre = g.Circle.Center; // Initialize the window with the start location. Window win = new Window(bcp); // Expand using the end location win.Union(ecp); // If the curve is completely within one quadrant, we're done. QuadVertex bc = new QuadVertex(centre, bcp); QuadVertex ec = new QuadVertex(centre, ecp); Quadrant qbc = bc.Quadrant; Quadrant qec = ec.Quadrant; if (qbc == qec) { if (g.IsClockwise) { if (bc.GetTanAngle() < ec.GetTanAngle()) { return(win); } } else { if (ec.GetTanAngle() < bc.GetTanAngle()) { return(win); } } } // Get the window of the circle IWindow circle = CircleGeometry.GetExtent(g.Circle); // If the curve is anticlockwise, switch the quadrants for BC & EC if (!g.IsClockwise) { Quadrant temp = qbc; qbc = qec; qec = temp; } // Expand the window, depending on which quadrants the start & // end points fall in. The lack of breaks in the inner switches // is intentional (e.g. if start & end both fall in Quadrant.NorthEast, // the window we want is the complete circle, having checked above // for the case where the arc is JUST in Quadrant.NorthEast). // Define do-nothing values for the Union's below double wx = win.Min.X; double wy = win.Min.Y; if (qbc == Quadrant.NE) { switch (qec) { case Quadrant.NE: win.Union(wx, circle.Max.Y); goto case Quadrant.NW; case Quadrant.NW: win.Union(circle.Min.X, wy); goto case Quadrant.SW; case Quadrant.SW: win.Union(wx, circle.Min.Y); goto case Quadrant.SE; case Quadrant.SE: win.Union(circle.Max.X, wy); break; } } else if (qbc == Quadrant.SE) { switch (qec) { case Quadrant.SE: win.Union(circle.Max.X, wy); goto case Quadrant.NE; case Quadrant.NE: win.Union(wx, circle.Max.Y); goto case Quadrant.NW; case Quadrant.NW: win.Union(circle.Min.X, wy); goto case Quadrant.SW; case Quadrant.SW: win.Union(wx, circle.Min.Y); break; } } else if (qbc == Quadrant.SW) { switch (qec) { case Quadrant.SW: win.Union(wx, circle.Min.Y); goto case Quadrant.SE; case Quadrant.SE: win.Union(circle.Max.X, wy); goto case Quadrant.NE; case Quadrant.NE: win.Union(wx, circle.Max.Y); goto case Quadrant.NW; case Quadrant.NW: win.Union(circle.Min.X, wy); break; } } else if (qbc == Quadrant.NW) { switch (qec) { case Quadrant.NW: win.Union(circle.Min.X, wy); goto case Quadrant.SW; case Quadrant.SW: win.Union(wx, circle.Min.Y); goto case Quadrant.SE; case Quadrant.SE: win.Union(circle.Max.X, wy); goto case Quadrant.NE; case Quadrant.NE: win.Union(wx, circle.Max.Y); break; } } return(win); }
public void Render(ISpatialDisplay display, IDrawStyle style) { if (this.category == CadastralLineCategory.Radial) style = new DottedStyle(style.LineColor); if (m_Center == null) style.Render(display, this.PositionArray); else { // radius less than zero may represent a counter-clockwise direction bool isClockwise = (this.radius > 0.0); // Define a circular arc that is assumed to run clockwise. ICircleGeometry circle = new CircleGeometry(m_Center.Geometry, Math.Abs(this.radius)); ICircularArcGeometry arc = new CircularArcGeometry(circle, m_From.Geometry, m_To.Geometry, isClockwise); // Assume clockwise, see what it looks like style.Render(display, arc); } /* else { if (!this.arcLengthSpecified) throw new ApplicationException("Cannot determine arc direction"); // Define a circular arc that is assumed to run clockwise. CircleGeometry circle = new CircleGeometry(m_Center.Geometry, this.radius); CircularArcGeometry arc = new CircularArcGeometry(circle, m_From.Geometry, m_To.Geometry, true); // Assume clockwise, see what it looks like new DrawStyle(Color.Red).Render(display, arc); //double arcLength = arc.Length.Meters; //double othLength = circle.Length.Meters; //// Get the arc length in meters (TODO: need to access file header to determine how to convert lengths) //if (Math.Abs(othLength - this.arcLength) < Math.Abs(arcLength - this.arcLength)) // arc.IsClockwise = false; } */ }
/// <summary> /// Calculates the intersection point. /// </summary> /// <param name="dir">Direction observation.</param> /// <param name="distance">Distance observation.</param> /// <param name="from">The point the distance was observed from.</param> /// <param name="usedefault">True if the default intersection is required (the one /// closer to the origin of the direction line). False for the other one (if any).</param> /// <param name="xsect">The position of the intersection (if any).</param> /// <param name="xsect1">The 1st choice intersection (if any).</param> /// <param name="xsect2">The 2nd choice intersection (if any).</param> /// <returns>True if intersections were calculated. False if the distance circles /// don't intersect.</returns> internal static bool Calculate(Direction dir, Observation distance, PointFeature from, bool usedefault, out IPosition xsect, out IPosition xsect1, out IPosition xsect2) { // Initialize intersection positions. xsect = xsect1 = xsect2 = null; // Get the distance. double dist = distance.GetDistance(from).Meters; if (dist < Constants.TINY) return false; // Form circle with a radius that matches the observed distance. ICircleGeometry circle = new CircleGeometry(from, dist); // See if there is actually an intersection between the direction & the circle. IPosition x1, x2; uint nx = dir.Intersect(circle, out x1, out x2); if (nx==0) return false; // If we have 2 intersections, and we need the non-default one, pick up the 2nd // intersection. If only 1 intersection, use that, regardless of the setting for // the "use default" flag. if (nx==2 && !usedefault) xsect = x2; else xsect = x1; // Return if the distance is an offset point. OffsetPoint offset = (distance as OffsetPoint); if (offset!=null) { xsect1 = x1; xsect2 = x2; return true; } // Reduce observed distance to the mapping plane. ISpatialSystem sys = CadastralMapModel.Current.SpatialSystem; dist = dist * sys.GetLineScaleFactor(from, xsect); // And calculate the exact intersection (like above)... // Form circle with a radius that matches the reduced distance. ICircleGeometry circlep = new CircleGeometry(from, dist); // See if there is actually an intersection between the direction & the circle. nx = dir.Intersect(circlep, out x1, out x2); if (nx==0) return false; // If we have 2 intersections, and we need the non-default one, pick up the 2nd // intersection. If only 1 intersection, use that, regardless of the setting for // the "use default" flag. if (nx==2 && !usedefault) xsect = x2; else xsect = x1; xsect1 = x1; xsect2 = x2; return true; }
/// <summary> /// Calculates intersection points. /// </summary> /// <param name="dist1">1st distance observation.</param> /// <param name="from1">The point the 1st distance was observed from.</param> /// <param name="dist2">2nd distance observation.</param> /// <param name="from2">The point the 2nd distance was observed from.</param> /// <param name="usedefault">True if the default intersection is required (the one that has the /// lowest bearing with respect to the 2 from points). False for the other one (if any).</param> /// <param name="xsect">The position of the intersection (if any).</param> /// <param name="xsect1">The 1st choice intersection (if any).</param> /// <param name="xsect2">The 2nd choice intersection (if any).</param> /// <returns>True if intersections were calculated. False if the distance circles /// don't intersect.</returns> internal static bool Calculate(Observation dist1, PointFeature from1, Observation dist2, PointFeature from2, bool usedefault, out IPosition xsect, out IPosition xsect1, out IPosition xsect2) { // Initialize intersection positions. xsect = xsect1 = xsect2 = null; // Get the 2 distances. double d1 = dist1.GetDistance(from1).Meters; double d2 = dist2.GetDistance(from2).Meters; if (d1 < Constants.TINY || d2 < Constants.TINY) return false; // Form circles with radii that match the observed distances. ICircleGeometry circle1 = new CircleGeometry(from1, d1); ICircleGeometry circle2 = new CircleGeometry(from2, d2); // See if there is actually an intersection between the two circles. IPosition x1, x2; uint nx = IntersectionHelper.Intersect(circle1, circle2, out x1, out x2); if (nx==0) return false; // If we have 2 intersections, and we need the non-default one, pick up the 2nd // intersection. If only 1 intersection, use that, regardless of the setting for // the "use default" flag. if (nx==2 && !usedefault) xsect = x2; else xsect = x1; // Return if both distances are offset points. OffsetPoint offset1 = (dist1 as OffsetPoint); OffsetPoint offset2 = (dist2 as OffsetPoint); if (offset1!=null && offset2!=null) { xsect1 = x1; xsect2 = x2; return true; } // Reduce observed distances to the mapping plane. ISpatialSystem sys = CadastralMapModel.Current.SpatialSystem; if (offset1==null) d1 = d1 * sys.GetLineScaleFactor(from1, xsect); if (offset2==null) d2 = d2 * sys.GetLineScaleFactor(from2, xsect); // And calculate the exact intersection (like above)... // Form circles with radii that match the observed distances. ICircleGeometry circle1p = new CircleGeometry(from1, d1); ICircleGeometry circle2p = new CircleGeometry(from2, d2); // See if there is still an intersection between the two circles. nx = IntersectionHelper.Intersect(circle1p, circle2p, out x1, out x2); if (nx==0) return false; // If we have 2 intersections, and we need the non-default one, pick up the 2nd // intersection. If only 1 intersection, use that, regardless of the setting for // the "use default" flag. if (nx==2 && !usedefault) xsect = x2; else xsect = x1; xsect1 = x1; xsect2 = x2; return true; }
/// <summary> /// Obtains the geometry for spans along this leg. /// </summary> /// <param name="bc">The position for the start of the leg. /// <param name="bcBearing">The bearing on entry into the leg.</param> /// <param name="sfac">Scale factor to apply to distances.</param> /// <param name="spans">Information for the spans coinciding with this leg.</param> /// <returns>The sections along this leg</returns> internal override ILineGeometry[] GetSpanSections(IPosition bc, double bcBearing, double sfac, SpanInfo[] spans) { // Can't do anything if the leg radius isn't defined if (m_Metrics.ObservedRadius == null) throw new InvalidOperationException("Cannot create sections for circular leg with undefined radius"); var result = new ILineGeometry[spans.Length]; // Use supplied stuff to derive info on the center and EC. IPosition center; IPosition ec; double bearingToBC; double ecBearing; GetPositions(bc, bcBearing, sfac, out center, out bearingToBC, out ec, out ecBearing); // Define the underlying circle ICircleGeometry circle = new CircleGeometry(PointGeometry.Create(center), BasicGeom.Distance(center, bc)); // Handle case where the leg is a cul-de-sac with no observed spans if (spans.Length == 1 && spans[0].ObservedDistance == null) { result[0] = new CircularArcGeometry(circle, bc, ec, m_Metrics.IsClockwise); return result; } /// Initialize scaling factor for distances in cul-de-sacs (ratio of the length calculated from /// the CA & Radius, versus the observed distances that were actually specified). For curves that /// are not cul-de-sacs, this will be 1.0 double culFactor = 1.0; if (m_Metrics.IsCulDeSac) { double obsv = PrimaryFace.GetTotal(); if (obsv > MathConstants.TINY) culFactor = Length.Meters / obsv; } IPosition sPos = bc; IPosition ePos = null; bool isClockwise = m_Metrics.IsClockwise; double radius = RadiusInMeters; double edist = 0.0; for (int i = 0; i < result.Length; i++, sPos = ePos) { // Add on the unscaled distance edist += spans[i].ObservedDistance.Meters; // Get the angle subtended at the center of the circle. We use // unscaled values here, since the scale factors would cancel out. // However, we DO apply any cul-de-sac scaling factor, to account // for the fact that the distance may be inconsistent with the // curve length derived from the CA and radius. For example, it // is possible that the calculated curve length=200, although the // total of the observed spans is somehow only 100. In that case, // if the supplied distance is 50, we actually want to use a // value of 50 * (200/100) = 100. double angle = (edist * culFactor) / radius; // Get the bearing of the point with respect to the center of the circle. double bearing; if (isClockwise) bearing = bearingToBC + angle; else bearing = bearingToBC - angle; // Calculate the position using the scaled radius. ePos = Geom.Polar(center, bearing, radius * sfac); result[i] = new CircularArcGeometry(circle, sPos, ePos, isClockwise); } return result; }