/// <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); }
/// <summary> /// Obtains the geometry for spans along an alternate face attached to this leg. /// </summary> /// <param name="start">The position for the start of the leg. /// <param name="end">The position for the end of the leg.</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 start, IPosition end, SpanInfo[] spans) { Debug.Assert(AlternateFace != null); // Get the desired length (in meters on the ground) double len = Geom.Distance(start, end); // Get the observed length (in meters on the ground) double obs = AlternateFace.GetTotal(); // Get the adjustment factor for stretching-compressing the observed distances. double factor = len / obs; // Get the bearing of the line. double bearing = Geom.BearingInRadians(start, end); return(GetSpanSections(start, bearing, factor, spans)); }
/// <summary> /// Calculates an angle that is parallel to this line (suitable for adding text) /// </summary> /// <param name="pos">A significant point on the line (the returned angle /// will be at a tangent at this point)</param> /// <returns>The rotation (in radians, clockwise from horizontal). Always greater /// than or equal to 0.0</returns> internal override double GetRotation(IPointGeometry pos) { // Get the bearing from the centre to the midpoint. IPosition center = m_Circle.Center; double bearing = Geom.BearingInRadians(center, pos); // If the midpoint is above the circle center, we get upside-down // text so rotate it the other way. if (pos.Y > center.Y) { return(bearing); } double angle = bearing - MathConstants.PI; if (angle < 0.0) { angle = bearing + MathConstants.PI; } return(angle); }
/// <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; }
/// <summary> /// Defines links for this face. A prior call to <c>SetLine</c> is required. /// </summary> /// <param name="prev">The preceding face (after it has been processed via a call to /// <c>SetLine</c>, Must refer to the same polygon as this face.</param> /// <returns>The links (the number of array elements will equal the value that came /// back from the <c>SetLine</c> call)</returns> internal PolygonLink[] CreateLinks(PolygonFace prev) { Debug.Assert(m_Polygon != null); Debug.Assert(m_Divider != null); Debug.Assert(prev.Divider != null); Debug.Assert(Object.ReferenceEquals(prev.Polygon, m_Polygon)); // Try to obtain a point feature at the beginning of this face. ISpatialIndex index = CadastralMapModel.Current.Index; PointFeature beginPoint = new FindPointQuery(index, m_Begin).Result; // If the polygon to the left of the this face? (if so, curve-related // things may need to be reversed below) bool isPolLeft = (m_Divider.Left == m_Polygon); // Default geometric info we need to define bool isCurveEnd = false; bool isRadial = false; double bearing = 0.0; double angle = 0.0; // Is the position at the beginning of this face the start // or end of a circular arc (points between 2 curves (on // compound curve) are NOT considered to be curve ends). Note // that the line could either be a CircularArc, or a topological // section based on a circular arc. ICircularArcGeometry prevArc = (prev.Divider.LineGeometry as ICircularArcGeometry); ICircularArcGeometry thisArc = (m_Divider.LineGeometry as ICircularArcGeometry); bool isPrevCurve = (prevArc != null); bool isThisCurve = (thisArc != null); isCurveEnd = (isPrevCurve != isThisCurve); // If we are dealing with a point between two curves try to // set the curve parameters and, if successful, we will treat // the point as a radial point. if (isPrevCurve && isThisCurve) { isRadial = true; // Get the curve parameters... IPosition prevcen = prevArc.Circle.Center; double prevrad = prevArc.Circle.Radius; IPosition thiscen = thisArc.Circle.Center; double thisrad = thisArc.Circle.Radius; // We only need to know the sense for one of the arcs bool iscw = thisArc.IsClockwise; // If the 2 curves do not have the same centre and radius, // see if they are really close (to the nearest centimeter // on the ground). In that case, use the average values for // the 2 curves. If they really are different curves, don't // treat this as a radial curve point -- treat it as a // curve end instead. IPosition centre = null; // The centre to actually use if (!(prevcen == thiscen && Math.Abs(prevrad - thisrad) < Constants.TINY)) { double xc1 = prevcen.X; double yc1 = prevcen.Y; double xc2 = thiscen.X; double yc2 = thiscen.Y; double dxc = xc2 - xc1; double dyc = yc2 - yc1; if (Math.Abs(dxc) < 0.01 && Math.Abs(dyc) < 0.01 && Math.Abs(prevrad - thisrad) < 0.01) { // Close enough centre = new Position(xc1 + dxc * 0.5, yc1 + dyc * 0.5); } else { isRadial = false; isCurveEnd = true; } } else { centre = prevcen; } // If the centre and radius were the same (or close enough), // define the radial bearing from the centre of the circle. // If the polygon is actually on the other side, reverse the // bearing so that it's directed INTO the polygon. if (isRadial) { Debug.Assert(centre != null); bearing = Geom.BearingInRadians(centre, m_Begin); angle = 0.0; if (iscw != isPolLeft) { bearing += Constants.PI; } } } // If we're not dealing with a radial curve (or we have curves that // don't appear to be radial) if (!isRadial) { // Get the clockwise angle from the last position of the // preceding face to the first position after the start // of this face. Since info is held in a clockwise cycle // around the polygon, this will always give us the exterior // angle. Turn reference = new Turn(m_Begin, prev.TailReference); angle = reference.GetAngleInRadians(this.HeadReference); // Define the bearing to use for projecting the point. It's // in the middle of the angle, but projected into the polygon. bearing = reference.BearingInRadians + angle * 0.5 + Constants.PI; } // Initialize the link at the start of the face List <PolygonLink> links = new List <PolygonLink>(); PolygonLink link = new PolygonLink(beginPoint, isCurveEnd, isRadial, bearing, angle); links.Add(link); // Initialize links for any extra points if (m_ExtraPoints != null) { // Intermediate points can never be curve ends isCurveEnd = false; // If the face is a curve, they're radial points isRadial = isThisCurve; // Note any curve info double radius; IPosition centre = null; bool iscw = false; if (isRadial) { Debug.Assert(m_Divider.Line.LineGeometry is ICircularArcGeometry); ICircularArcGeometry arc = (m_Divider.Line.LineGeometry as ICircularArcGeometry); centre = arc.Circle.Center; radius = arc.Circle.Radius; iscw = arc.IsClockwise; angle = 0.0; } for (uint i = 0; i < m_ExtraPoints.Length; i++) { //IPointGeometry loc = m_ExtraPoints[i].Geometry; PointFeature loc = m_ExtraPoints[i]; // Figure out the orientation bearing for the point if (isRadial) { Debug.Assert(centre != null); bearing = Geom.BearingInRadians(centre, loc); if (iscw != isPolLeft) { bearing += Constants.PI; } } else { // Get the exterior clockwise angle IPointGeometry back; IPointGeometry fore; if (i == 0) { back = m_Begin; } else { back = m_ExtraPoints[i - 1].Geometry; } if (i == (m_ExtraPoints.Length - 1)) { fore = m_End; } else { fore = m_ExtraPoints[i + 1].Geometry; } Turn reference = new Turn(loc, back); angle = reference.GetAngleInRadians(fore); // Define the bearing to use for projecting the point. It's // in the middle of the angle, but projected into the polygon. bearing = reference.BearingInRadians + angle * 0.5 + Constants.PI; } link = new PolygonLink(m_ExtraPoints[i], isCurveEnd, isRadial, bearing, angle); links.Add(link); } } return(links.ToArray()); }
/// <summary> /// Intersects a line segment with a circle. /// </summary> /// <param name="centre">Position of the circle's centre.</param> /// <param name="radius">Radius of the circle.</param> /// <param name="start">Start of segment.</param> /// <param name="end">End of segment.</param> /// <param name="x1">First intersection (if any).</param> /// <param name="x2">Second intersection (if any).</param> /// <param name="istangent">Is segment a tangent to the circle? This can be true only if /// ONE intersection is returned.</param> /// <param name="online">TRUE if the intersection must lie ON the segment.</param> /// <returns>The number of intersections found (0, 1, or 2).</returns> internal static uint IntersectCircle(IPosition centre , double radius , IPosition start , IPosition end , out IPosition ox1 , out IPosition ox2 , out bool istangent , bool online) { // Initialize the return info. Position x1 = new Position(0, 0); Position x2 = new Position(0, 0); ox1 = x1; ox2 = x2; istangent = false; // Get the bearing of this segment. double bearing = Geom.BearingInRadians(start, end); // The equation of each line is given in parametric form as: // // x = xo + f * r // y = yo + g * r // // where xo,yo is the from point // and f = sin(bearing) // and g = cos(bearing) // and r is the distance along the line. double g = Math.Cos(bearing); double f = Math.Sin(bearing); double fsq = f * f; double gsq = g * g; double startx = start.X; double starty = start.Y; double dx = centre.X - startx; double dy = centre.Y - starty; double fygx = f * dy - g * dx; double root = radius * radius - fygx * fygx; // Check for no intersection. if (root < -Constants.TINY) { return(0); } // We've either got 1 or 2 intersections ... // If the intersection has to be ON the line, we'll need // the length of the line segment. double seglen = 0.0; if (online) { seglen = BasicGeom.Distance(start, end); } // Check for tangential intersection. if (root < Constants.TINY) { double xdist = f * dx + g * dy; if (online && (xdist < 0.0 || xdist > seglen)) { return(0); } x1.X = startx + f * xdist; x1.Y = starty + g * xdist; istangent = true; return(1); } // That leaves us with 2 intersections, although one or both of // them may not actually fall on the segment. double fxgy = f * dx + g * dy; root = Math.Sqrt(root); double dist1 = (fxgy - root); double dist2 = (fxgy + root); if (online) { uint nok = 0; if (dist1 > 0.0 && dist1 < seglen) { x1.X = startx + f * dist1; x1.Y = starty + g * dist1; nok = 1; } if (dist2 > 0.0 && dist2 < seglen) { if (nok == 0) { x1.X = startx + f * dist2; x1.Y = starty + g * dist2; return(1); } x2.X = startx + f * dist2; x2.Y = starty + g * dist2; return(2); } return(nok); } // Doesn't need to be ON the segment. x1.X = startx + f * dist1; x1.Y = starty + g * dist1; x2.X = startx + f * dist2; x2.Y = starty + g * dist2; return(2); }