/// <summary> /// Gets the position of a point on the circular leg. /// </summary> /// <param name="dist">The (unscaled) distance to the desired point.</param> /// <returns>The position.</returns> IPosition GetPoint(double dist, IPosition center, double radius, double culFactor, double bearingToBC, double sfac) { // 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 = (dist * culFactor) / radius; // Get the bearing of the point with respect to the center of the circle. double bearing; if (m_Metrics.IsClockwise) { bearing = bearingToBC + angle; } else { bearing = bearingToBC - angle; } // Calculate the position using the scaled radius. return(Geom.Polar(center, bearing, radius * sfac)); }
/// <summary> /// Gets the orientation point for a line. This is utilized to form /// network topology at the ends of a topological line. /// </summary> /// <param name="fromStart">True if the orientation from the start of the line is /// required. False to get the end orientation.</param> /// <param name="crvDist">Orientation distance for circular arcs (if a value of /// zero (or less) is specified, a "reasonable" orientation distance will be used - /// currently 5 metres on the ground). /// </param> /// <returns>The orientation point.</returns> internal override IPosition GetOrient(bool fromStart, double crvDist) { // Point to the BC or EC, depending on what we are orienting. IPosition loc = (fromStart ? Start : End); // Get the bearing of the location from the center of the circular arc IPointGeometry centre = Circle.Center; double bearing = Geom.BearingInRadians(centre, loc); // Get the distance to the orientation point. It should not be // any further than the length of the arc. We use a fixed // orientation distance of 5 metres on the ground for now. double dist; if (crvDist < Constants.TINY) { dist = Math.Min(5.0, Length.Meters); } else { dist = Math.Min(crvDist, Length.Meters); } // If we are coming from the end of the arc, use a negated // distance to get the direction right. if (!fromStart) { dist = -dist; } // Add the angle that subtends the orientation distance (or // subtract if the arc goes anti-clockwise). double radius = Circle.Radius; if (m_IsClockwise) { bearing += (dist / radius); } else { bearing -= (dist / radius); } // Figure out the orientation point from the new bearing. return(Geom.Polar(centre, bearing, radius)); }
/// <summary> /// Gets the point on this line that is closest to a specified position. /// </summary> /// <param name="p">The position to search from.</param> /// <param name="tol">Maximum distance from line to the search position</param> /// <returns>The closest position (null if the line is further away than the specified /// max distance)</returns> internal override IPosition GetClosest(IPointGeometry p, ILength tol) { // Get the distance from the centre of the circle to the search point. IPointGeometry center = m_Circle.Center; double dist = Geom.Distance(center, p); // Return if the search point is beyond tolerance. double radius = m_Circle.Radius; double diff = Math.Abs(dist - radius); if (diff > tol.Meters) { return(null); } // If the vertex lies in the curve sector, the closest position // is along the bearing from the centre through the search // position. Otherwise the closest position is the end of // the curve that's closest (given that it's within tolerance). if (CircularArcGeometry.IsInSector(this, p, 0.0)) { double bearing = Geom.BearingInRadians(center, p); return(Geom.Polar(center, bearing, radius)); } double d1 = Geom.DistanceSquared(p, BC); double d2 = Geom.DistanceSquared(p, EC); double t = tol.Meters; if (Math.Min(d1, d2) < (t * t)) { if (d1 < d2) { return(BC); } else { return(EC); } } return(null); }
private Feature ImportName(Ntx.Name name, Operation creator) { /* * // Get pointer to the applicable map theme * CeTheme theme(Name.GetTheme()); * CeTheme* pTheme = theme.AddTheme(); * * // Get pointer to the entity type. * GRAPHICSTYPE geom = ANNOTATION; * if ( Name.IsLabel() ) geom = POLYGON; * CeEntity* pEntity = AddEntity(Name.GetpFeatureCode(),pTheme,geom); */ IEntity entity = GetEntityType(name, SpatialType.Text); // Get the text string string text = name.Text; // Get the position of the centre of the 1st character Ntx.Position pos = name.Position(0); IPosition vcentre = new Position(pos.Easting, pos.Northing); // Get text metrics float height = name.Height; float spacing = name.Spacing; float rotation = name.Rotation; // Calculate the top left corner of the first character using // the text metrics we just got ... // Get the width of the first character. For names that contain // only one character, the spacing we have will be zero, so in // that case, deduce the width of the character via the covering // rectangle. float charwidth = spacing; if (charwidth < Constants.TINY) { // Get the covering rectangle. Ntx.Position nw = name.NorthWest; Ntx.Position se = name.SouthEast; // And get the dimensions. double dx = se.Easting - nw.Easting; double dy = nw.Northing - se.Northing; // If the cover is screwed up, assume the width is 80% of the text height. if (dy < Constants.TINY) { charwidth = (float)(height * 0.8); } else { charwidth = (float)(height * (dx / dy)); } } // Define the bearing from bottom to top of the text. double vbear = (double)rotation; // Get position directly above the centre of the 1st char. IPosition above = Geom.Polar(vcentre, vbear, 0.5 * (double)height); // Define the bearing from the point we just got to the // start of the text string. double hbear = vbear - Constants.PIDIV2; // Back up half a character to get the initial corner. PointGeometry topleft = new PointGeometry(Geom.Polar(above, hbear, 0.5 * (double)charwidth)); IFont font = null; double width = (double)text.Length * charwidth; TextFeature result = null; if (name.IsLabel) { // Create key text string keystr = name.Text; KeyTextGeometry kt = new KeyTextGeometry(topleft, font, height, width, rotation); InternalIdValue id = CadastralMapModel.Current.WorkingSession.AllocateNextId(); result = new TextFeature(creator, id, entity, kt); kt.Label = result; result.SetTopology(true); // Define the label's foreign ID and form a two-way association ForeignId fid = GetFeatureId(keystr); Debug.Assert(fid != null); fid.Add(result); // Remember the reference position of the label. Ntx.Position xp = name.RefPosition; IPointGeometry pp = new PointGeometry(xp.Easting, xp.Northing); result.SetPolPosition(pp); } else { // Create a miscellaneous text label. MiscTextGeometry mt = new MiscTextGeometry(text, topleft, font, height, width, rotation); InternalIdValue id = CadastralMapModel.Current.WorkingSession.AllocateNextId(); result = new TextFeature(creator, id, entity, mt); result.SetTopology(false); } return(result); }
/// <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); }
/// <summary> /// Obtains annotation for this line. /// </summary> /// <param name="dist">The observed distance (if any).</param> /// <param name="drawObserved">Draw observed distance? Specify <c>false</c> for /// actual distance.</param> /// <returns>The annotation (null if it cannot be obtained)</returns> Annotation GetAnnotation(Distance dist, bool drawObserved) { // @devnote This function may not be that hot for curves that // are complete circles. At the moment though, I can't see why // we'd be drawing a distance alongside a circle. // Get the length of the arc double len = this.Length.Meters; // Get the string to output. string distr = GetDistance(len, dist, drawObserved); if (distr == null) { return(null); } // Get the mid-point of this arc. IPosition mid; this.GetPosition(new Length(len * 0.5), out mid); // Get the bearing from the center to the midpoint. IPosition center = m_Circle.Center; double bearing = BasicGeom.BearingInRadians(center, mid); // Get the height of the text, in meters on the ground. double grheight = EditingController.Current.LineAnnotationStyle.Height; // We will offset by 20% of the height (give a bit of space // between the text and the line). //double offset = grheight * 0.2; // ...looks fine with no offset (there is a space, but it's for descenders // that aren't there since we're dealing just with numbers). double offset = 0.0; // Get the rotation of the text. double rotation = bearing - MathConstants.PI; // If the midpoint is above the circle centre, we get upside-down // text so rotate it the other way. If we don't switch the // rotation, we need to make an extra shift, because the text // is aligned along its baseline. // This depends on whether the annotation is on the default // side or not. // Not sure about the following... (c.f. revised handling in SegmentGeometry) /* * if (mid.Y > center.Y) * { * rotation += MathConstants.PI; * if (isFlipped) * offset = -0.9 * grheight; * } * else * { * if (isFlipped) * offset = -offset; * else * offset = 0.9 * grheight; // 1.0 * grheight is too much * } */ // ...try this instead if (mid.Y > center.Y) { rotation += MathConstants.PI; } else { offset = 1.3 * grheight; // push the text to the outer edge of the arc } if (dist != null && dist.IsAnnotationFlipped) { rotation += MathConstants.PI; // and may need to adjust offset... } // Project to the offset point. IPosition p = Geom.Polar(center, bearing, m_Circle.Radius + offset); Annotation result = new Annotation(distr, p, grheight, rotation); result.FontStyle = FontStyle.Italic; return(result); }
/// <summary> /// Adjusts the path (Helmert adjustment). /// </summary> /// <param name="dN">Misclosure in northing.</param> /// <param name="dE">Misclosure in easting.</param> /// <param name="precision">Precision denominator (zero if no adjustment needed).</param> /// <param name="length">Total observed length.</param> /// <param name="rotation">The clockwise rotation to apply (in radians).</param> /// <param name="sfac">The scaling factor to apply.</param> void Adjust(out double dN, out double dE, out double precision, out double length, out double rotation, out double sfac) { dN = dE = precision = length = rotation = 0.0; sfac = 1.0; // Initialize position to the start of the path, corresponding to the initial // un-adjusted end point. IPosition start = m_From; IPosition gotend = new Position(m_From); // Initial bearing is due north. double bearing = 0.0; // Go through each leg, updating the end position, and getting // the total path length. foreach (Leg leg in m_Legs) { length += leg.Length.Meters; leg.Project(ref gotend, ref bearing, sfac); } // Get the bearing and distance of the end point we ended up with. double gotbear = Geom.BearingInRadians(m_From, gotend); double gotdist = Geom.Distance(m_From, gotend); // Get the bearing and distance we want. double wantbear = Geom.BearingInRadians(m_From, m_To); double wantdist = Geom.Distance(m_From, m_To); // Figure out the rotation. rotation = wantbear - gotbear; // Rotate the end point we got. gotend = Geom.Rotate(m_From, gotend, new RadianValue(rotation)); // Calculate the line scale factor. double linefac = m_From.MapModel.SpatialSystem.GetLineScaleFactor(m_From, gotend); // Figure out where the rotated end point ends up when we apply the line scale factor. gotend = Geom.Polar(m_From, wantbear, gotdist * linefac); // What misclosure do we have? dN = gotend.Y - m_To.Y; dE = gotend.X - m_To.X; double delta = Math.Sqrt(dN * dN + dE * dE); // What's the precision denominator (use a value of 0 to denote an exact match). if (delta > MathConstants.TINY) { precision = wantdist / delta; } else { precision = 0.0; } // Figure out the scale factor for the adjustment (use a value of 0 if the start and end // points are coincident). The distances here have NOT been adjusted for the line scale factor. if (gotdist > MathConstants.TINY) { sfac = wantdist / gotdist; } else { sfac = 0.0; } // Remember the rotation and scaling factor m_IsAdjusted = true; m_Rotation = rotation; m_ScaleFactor = sfac; m_Precision = precision; }