// @parm The point feature that correspond to the search location. // Defined only if a better intersection is actually found. /// <summary> /// Calculates whether the supplied point is closer to the intersections defined /// in this results object. /// </summary> /// <param name="point">The point to test</param> /// <param name="mindsq">The best distance (squared) so far. This should be initially passed in /// as a very big value.</param> /// <param name="xsect">The intersection. This will be defined only if a better intersection is /// actually found.</param> /// <returns>True if a <paramref name="point"/> is closer to an intersection (in that case, /// <paramref name="mindsq"/> and <paramref name="xsect"/> will have been updated). False /// if the point is no closer.</returns> /// <remarks>This replaces the old CeArc.GetCloserPoint method</remarks> internal bool GetCloserPoint(IPosition point, ref double mindsq, ref IPosition xsect) { // Scan the set of intersections to find the closest, disallowing // any intersections that are coincident with the search location // (to within some very small tolerance). IPosition xi; if (!GetClosest(point, out xi, Constants.XYTOLSQ)) { return(false); } // Get the distance (squared) to the intersection, double dsq = Geom.DistanceSquared(xi, point); if (mindsq < dsq) { return(false); } // The newly found intersection is the best so far. mindsq = dsq; xsect = xi; return(true); }
/// <summary> /// Returns the distance (squared) between 2 link points. /// </summary> /// <param name="other">The other link to get the distance to.</param> /// <returns></returns> internal double DistanceSquared(PolygonLink other) { // 22-MAR-00 There may be no point at a corner. if (m_Point == null || other.Point == null) { return(0.0); } else { return(Geom.DistanceSquared(m_Point, other.Point)); } }
/// <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> /// 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> /// 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> /// 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); }