/// <summary> /// Cuts back a horizontal line segment to the closest intersection with this line. /// Used in point in polygon. /// </summary> /// <param name="s">Start of horizontal segment.</param> /// <param name="e">End of segment (will be modified if segment intersects this line)</param> /// <param name="status">Return code indicating whether an error has arisen (returned /// as 0 if no error).</param> /// <returns>True if the horizontal line was cut back.</returns> internal override bool GetCloser(IPointGeometry s, ref PointGeometry e, out uint status) { status = 0; // Remember the initial end of segment PointGeometry initEnd = new PointGeometry(e); // Represent the horizontal segment in a class of its own HorizontalRay hseg = new HorizontalRay(s, e.X - s.X); if (!hseg.IsValid) { status = 1; return(false); } // Get relative position code for the start of the line. If it's // somewhere on the horizontal segment, cut the line back. byte scode = Geom.GetPositionCode(Start, s, e); if (scode == 0) { e = new PointGeometry(Start); } // Get the position code for the end of the line segment byte ecode = Geom.GetPositionCode(End, s, e); // If it's coincident with the horizontal segment, cut the // line back. Otherwise see whether there is any potential // intersection to cut back to. if (ecode == 0) { e = new PointGeometry(End); } else if ((scode & ecode) == 0) { IPosition x = null; if (hseg.Intersect(Start, End, ref x)) { e = new PointGeometry(x); } } // Return flag to indicate whether we got closer or not. return(!e.IsCoincident(initEnd)); }
/// <summary> /// Delegate that's called whenever the index finds a line with an extent that /// overlaps the query window. /// </summary> /// <param name="item">The item to process (expected to be some sort of <c>IFeature</c>)</param> /// <returns>True (always), meaning the query should continue.</returns> private bool OnQueryHit(ISpatialObject item) { if (item is Circle) { // Confirm the circle is truly within tolerance Circle c = (item as Circle); double rad = c.Radius; double dist = Geom.Distance(c.Center, m_Position); if (Math.Abs(rad - dist) < m_Tolerance) { m_Result.Add(c); } } return(true); }
/// <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> /// Checks whether this link object intersects another one. /// </summary> /// <param name="other">The other link to check.</param> /// <returns> /// True if there is an intersection. False if: /// /// 1. The other link is THIS link. /// 2. This link is the end of a link. /// 3. This link has no link. /// 4. The other link is the end of any link. /// 5. The other link is not linked. /// 6. There is no intersection. /// </returns> internal bool IsIntersected(PolygonLink other) { // The other object can't be THIS object. if (Object.ReferenceEquals(other, this)) { return(false); } // The other can't be linked to this. if (Object.ReferenceEquals(other, m_Link)) { return(false); } // Both objects must be linked. if (m_Link == null || other.Link == null) { return(false); } // It must be the START of both links. if (m_IsEnd || other.IsEnd) { return(false); } // Get the position of this link. double xk = m_Point.X; double yk = m_Point.Y; double xl = m_Link.Point.X; double yl = m_Link.Point.Y; // Get the position of the other link. double xm = other.Point.X; double ym = other.Point.Y; double xn = other.Link.Point.X; double yn = other.Link.Point.Y; // Return if there is any intersection along the length of the test segments. double xi, yi; return(Geom.CalcIntersect(xk, yk, xl, yl, xm, ym, xn, yn, out xi, out yi, true) != 0); }
/// <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> /// Checks whether this closed shape overlaps (encloses) a point. /// </summary> /// <param name="point">The position to check</param> /// <returns>True if the shape overlaps the point</returns> internal bool IsOverlap(IPointGeometry point) { return(m_Extent.IsOverlap(point) && Geom.IsPointInClosedShape(this.Data, point)); }
/// <summary> /// Checks whether a position is coincident with a line segment, using a tolerance that's /// consistent with the resolution of data. /// </summary> /// <param name="testX">The position to test</param> /// <param name="testY"></param> /// <param name="xs">Start of line segment.</param> /// <param name="ys"></param> /// <param name="xe">End of line segment.</param> /// <param name="ye"></param> /// <returns>True if the distance from the test position to the line segment is less /// than <c>Constants.XYTOL</c> (3 microns on the ground)</returns> // Redundant? private static bool IsCoincident(double testX, double testY, double xs, double ys, double xe, double ye) { return(Geom.DistanceSquared(testX, testY, xs, ys, xe, ye) < Constants.XYTOLSQ); }
/// <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); }
/// <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> /// 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> /// <param name="doLast">Specify true to consider the last segment. False to ignore the /// last segment.</param> /// <returns>The closest position (null if the line is further away than the specified /// max distance)</returns> IPosition FindClosest(IPointGeometry p, ILength tol, bool doLast) { IPointGeometry[] data = Data; // Initial "best" distance squared cannot be greater than the square of the match tolerance. double tm = tol.Meters; double bestdsq = tm * tm; // Best segment number so far (valid segment numbers start at 1). int best = 0; // Pull out the XY of the search vertex double vx = p.X; double vy = p.Y; // Get start of the initial line segment double x1 = data[0].X; double y1 = data[0].Y; // Only do the last segment when required. int nv = data.Length; if (!doLast) { nv--; } for (int i = 1; i < nv; i++) { // Pick up the end of the segment & get the window of // the segment, expanded by the match tolerance. double x2 = data[i].X; double y2 = data[i].Y; double xmin = Math.Min(x1, x2) - tm; double ymin = Math.Min(y1, y2) - tm; double xmax = Math.Max(x1, x2) + tm; double ymax = Math.Max(y1, y2) + tm; // If the search vertex falls within the expanded window, // and the distance (squared) to the perpendicular point // is better than what we already got, remember the index // number of this segment. if (vx > xmin && vx < xmax && vy > ymin && vy < ymax) { double dsq = Geom.DistanceSquared(vx, vy, x1, y1, x2, y2); if (dsq < bestdsq) { bestdsq = dsq; best = i; } } // End of segment is start of next one x1 = x2; y1 = y2; } // Return if we did not locate a suitable point if (best == 0) { return(null); } // Get the position of the perpendicular point. double xp, yp; IPointGeometry s = data[best - 1]; IPointGeometry e = data[best]; Geom.GetPerpendicular(vx, vy, s.X, s.Y, e.X, e.Y, out xp, out yp); return(new Position(xp, yp)); }
/// <summary> /// Intersects this multi-segment with itself. Only SIMPLE intersections will be /// found. There are no special checks for multi-segments that graze themselves. /// </summary> /// <param name="xsect">The intersection results.</param> /// <returns>The number of self-intersections found.</returns> uint SelfIntersect(IntersectionResult xsect) { uint nx = 0; // Get an array of cumulative distances for each segment. IPointGeometry[] data = this.Data; double[] cumdist = GetCumDist(data); // Note start of initial segment (treat as the end of some imaginary line prior to the start). double xs; double ys; double xe = data[0].X; double ye = data[0].Y; // How many line segments have we got? int nseg = data.Length - 1; // Loop through each segment, intersecting it with all subsequent // segments, except for the one that immediately follows. for (int iseg = 1; iseg <= (nseg - 2); iseg++) { // The start of this segment is the end of the previous one. xs = xe; ys = ye; // Get the position of the end of the test segment xe = data[iseg].X; ye = data[iseg].Y; // Compare against subsequent segments (except the next one) for (int jseg = iseg + 2; jseg <= nseg; jseg++) { IPointGeometry start = data[jseg - 1]; IPointGeometry end = data[jseg]; double xi, yi; if (Geom.CalcIntersect(start.X, start.Y, end.X, end.Y, xs, ys, xe, ye, out xi, out yi, true) != 0) { // Define distance to the intersection on the i-segment double dx = xi - xs; double dy = yi - ys; double ilen = cumdist[iseg - 1] + Math.Sqrt(dx * dx + dy * dy); // Likewise for the j-segment dx = xi - start.X; dy = yi - start.Y; double jlen = cumdist[jseg - 1] + Math.Sqrt(dx * dx + dy * dy); // Append TWO intersections. xsect.Append(xi, yi, ilen); xsect.Append(xi, yi, jlen); nx += 2; } } } // Sort the intersections (DON'T set new sort values). xsect.Sort(false); // Return the number of intersections return(nx); }
/// <summary> /// Finds the line segment that a position lies on (if any). /// </summary> /// <param name="data">The data to search</param> /// <param name="doFirst">Should the start of the first segment be considered? (default=true)</param> /// <param name="startIndex">The array index of the element in <c>data</c> where /// the search should start (default=0).</param> /// <param name="find">The position to find</param> /// <returns>The index of the segment which the search point is coincident with (-1 if not found)</returns> static int FindSegment(IPointGeometry[] data, bool doFirst, int startIndex, IPointGeometry find) { // There have to be at least 2 positions. if (data.Length < 2) { return(-1); } // Get the position of the vertex to find double x = find.X; double y = find.Y; // Loop through each line segment. The search point must lie somewhere // on, or within the window of the segment. int seg; // Index to the vertex at the END of a segment double xs, ys; // Start of segment double xe, ye; // End of segment // If the first point is to be excluded, and it matches the // search position, start on the second line segment instead. if (!doFirst && data[startIndex].IsCoincident(find)) { xs = data[startIndex + 1].X; ys = data[startIndex + 1].Y; seg = startIndex + 2; } else { xs = data[startIndex].X; ys = data[startIndex].Y; seg = startIndex + 1; } for (; seg < data.Length; seg++) { // Get the easting and northing at the end of the segment xe = data[seg].X; ye = data[seg].Y; // If the point to find lies within the window of the segment, // determine the perpendicular distance (squared) between the // vertex and the line segment. If within tol, return the // current segment number. if (x >= Math.Min(xs, xe) - Constants.XYTOL && x <= Math.Max(xs, xe) + Constants.XYTOL && y >= Math.Min(ys, ye) - Constants.XYTOL && y <= Math.Max(ys, ye) + Constants.XYTOL) { if (Geom.DistanceSquared(x, y, xs, ys, xe, ye) < Constants.XYTOLSQ) { return(seg - 1); } } // The end of this line segment is the start of the next one. xs = xe; ys = ye; } // No match found return(-1); }
/// <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> /// 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> /// 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; }
void CheckPts(string ptsFileName, CadastralMapModel mm) { if (!File.Exists(ptsFileName)) { return; } var badList = new List <CheckData>(); foreach (string s in File.ReadAllLines(ptsFileName)) { string[] items = s.Split(','); uint id = UInt32.Parse(items[0]); double x = Double.Parse(items[1]); double y = Double.Parse(items[2]); Position a = new Position(x, y); PointFeature p = mm.Find <PointFeature>(new InternalIdValue(id)); if (p != null) { double delta = Geom.Distance(a, p); if (delta > 0.001) { badList.Add(new CheckData() { Point = p, Delta = delta }); } } } // Obtain the calculation sequence Operation[] calcs = mm.GetCalculationSequence(); var editOrder = new Dictionary <uint, uint>(); for (int i = 0; i < calcs.Length; i++) { editOrder.Add(calcs[i].EditSequence, (uint)i); } foreach (CheckData cd in badList) { cd.CalculationOrder = editOrder[cd.Point.Creator.EditSequence]; } badList.Sort((A, B) => A.CalculationOrder.CompareTo(B.CalculationOrder)); using (StreamWriter sw = File.CreateText(ptsFileName + ".check")) { // Dump out the calc order //foreach (Operation op in calcs) // sw.WriteLine(String.Format("Edit={0} Order={1} Type={2}", op.EditSequence, editOrder[op.EditSequence], op.EditId)); sw.WriteLine("Number of points>0.001 = " + badList.Count); foreach (CheckData cd in badList) { sw.WriteLine(String.Format("Order={0} Id={1} Delta={2:0.000}", cd.CalculationOrder, cd.Point.InternalId.ItemSequence, cd.Delta)); } } }
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); }
static bool GetPosition(IPosition[] line, double d, out IPosition result) { if (line.Length < 2) { throw new Exception("Line contains fewer than 2 positions"); } // Check for invalid distance if (d < 0.0) { result = line[0]; return(false); } double walked = 0.0; // Distance walked so far // Loop through the segments until we get to a distance that // exceeds the required distance. for (int i = 1; i < line.Length; i++) { // Update total length walked to end of segment double seglen = Geom.Distance(line[i - 1], line[i]); walked += seglen; // If the accumulated distance exceeds the required distance, // the point we want is somewhere in the current segment. if (walked >= d) { double ratio = (walked - d) / seglen; // Check whether the ratio yields a length that matches segment length to within // the order of the data precision (1 micron). If so, return the start of the segment. // If you don't do this, minute shifts occur when points are inserted into the // multisegment. This means that if you repeat the GetPosition call with the SAME distance, // you may actually get a point that is fractionally different from the initially returned // point. if ((seglen - ratio * seglen) < 0.000002) { result = line[i - 1]; } else { // Make damn sure! double xs = line[i - 1].X; double ys = line[i - 1].Y; double xe = line[i].X; double ye = line[i].Y; double dx = xe - xs; double dy = ye - ys; double xres = xe - ratio * dx; double yres = ye - ratio * dy; if (Math.Abs(xres - xe) < 0.000002 && Math.Abs(yres - ye) < 0.000002) { result = line[i]; } else { result = new Position(xres, yres); } } return(true); } } // Got to the end, so the distance is too much result = line[line.Length - 1]; return(false); }