/// <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> /// 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> /// Constructor (that does a lot of stuff) /// </summary> /// <param name="polygon">The polygon being subdivided.</param> internal PolygonSub(Polygon polygon) { m_Polygon = polygon; m_Faces = null; m_Links = null; // Get the boundaries defining the exterior edge of the polygon IDivider[] lines = polygon.Edge; if (lines == null || lines.Length == 0) { return; } // Allocate an array for holding info on each polygon face, // and associate each one with the divider that acts as the primary face. uint numLink = 0; m_Faces = new PolygonFace[lines.Length]; for (int i = 0; i < lines.Length; i++) { m_Faces[i] = new PolygonFace(); numLink += m_Faces[i].SetDivider(m_Polygon, lines[i]); } // Create an array of link objects (one for each point) if (numLink == 0) { return; } m_Links = new PolygonLink[numLink]; // Initialize the links for first face int nDone = 0; PolygonLink[] links = m_Faces[0].CreateLinks(m_Faces[m_Faces.Length - 1]); Array.Copy(links, m_Links, links.Length); nDone += links.Length; // Initialize links for each subsequent face for (int i = 1; i < m_Faces.Length; i++) { links = m_Faces[i].CreateLinks(m_Faces[i - 1]); for (int j = 0; j < links.Length; j++) { m_Links[nDone + j] = links[j]; } nDone += links.Length; } Debug.Assert(nDone == m_Links.Length); // Assign a side number to each link position uint nside = 0; // No side number assigned so far. uint ncorn = 0; // Number of corners for (int i = 0; i < m_Links.Length; i++) { // Skip if we are dealing with a corner. if (m_Links[i].IsCorner()) { ncorn++; continue; } // Skip if the side number has already been assigned. if (m_Links[i].Side != 0) { continue; } // Assign next side number. nside++; // Continue till we reach a corner, assigning intermediate // faces the same side number. for (int j = i; j < m_Links.Length; j++) { if (m_Links[j].IsCorner()) { break; } m_Links[j].Side = nside; } // If we just assigned a side number to the very first face, // loop back from the end of the array. if (i == 0) { for (int k = m_Links.Length - 1; k > 0; k--) { if (m_Links[k].IsCorner()) { break; } m_Links[k].Side = nside; } } } // Return if we only have one side (or less) // if ( nside<2 ) return; // Define the max angular difference (10 degrees, but in radians) double ANGTOL = (10.0 * Constants.DEGTORAD); // Process all the radial faces first. for (uint i = 0; i < m_Links.Length; i++) { if (m_Links[i].IsRadial) { this.SetLink(i, ANGTOL); } } // For each point that is not a corner or a curve end, cycle // through all the subsequent points, looking for a point to // connect to. Don't try to do curve end points just yet. for (uint i = 0; i < m_Links.Length; i++) { if (!m_Links[i].IsRadial && !m_Links[i].IsCurveEnd) { this.SetLink(i, ANGTOL); } } // Process any points at the end of curves. for (uint i = 0; i < m_Links.Length; i++) { if (m_Links[i].IsCurveEnd) { this.SetLink(i, ANGTOL); } } // Eliminate self-intersections ... // Start by counting the number of intersections each link has. uint maxnx = 0; for (uint i = 0; i < m_Links.Length; i++) { uint nx = this.SetIntersectionCount(i); maxnx = Math.Max(maxnx, nx); } uint nloop = 0; // Number of loops (just in case of infinite loop). while (maxnx > 0) { // Find a link with the max number of intersections. int rem = -1; for (int i = 0; i < m_Links.Length; i++) { if (m_Links[i].NumIntersect == maxnx) { rem = i; break; } } // We SHOULD have found something. if (rem < 0) { throw new Exception("PolygonSub: Unexpected intersection count"); } // If the count was greater than one, just remove the link. Otherwise get the // thing that was intersected. If one is a radial or a curve end, & the other isn't, // drop the radial. Otherwise drop the one with the poorer link angle. if (maxnx > 1) { m_Links[rem].SetLink(null); } else { PolygonLink pLink1 = m_Links[rem]; PolygonLink pLink2 = null; for (int i = 0; pLink2 != null && i < m_Links.Length; i++) { if (pLink1.IsIntersected(m_Links[i])) { pLink2 = m_Links[i]; } } if (pLink2 == null) { throw new Exception("PolygonSub: Intersection not found"); } // Check if radial. bool rad1 = pLink1.IsRadial || pLink1.Link.IsRadial; bool rad2 = pLink2.IsRadial || pLink2.Link.IsRadial; // Check if end of curves. bool end1 = pLink1.IsCurveEnd || pLink1.Link.IsCurveEnd; bool end2 = pLink2.IsCurveEnd || pLink2.Link.IsCurveEnd; // We treat radials and end of curves the same. bool curve1 = (rad1 || end1); bool curve2 = (rad2 || end2); // If one is a curve-related and the other isn't, drop // the one that's curve-related. if (curve1 != curve2) { if (curve1) { pLink1.SetLink(null); } else { pLink2.SetLink(null); } } else { // Neither is curve related, or both are. Drop the // one with the poorer link angle. if (pLink1.LinkAngle > pLink2.LinkAngle) { pLink1.SetLink(null); } else { pLink2.SetLink(null); } } } // Rework the intersect counts where necessary. maxnx = 0; for (uint i = 0; i < m_Links.Length; i++) { if (m_Links[i].NumIntersect > 0) { maxnx = Math.Max(maxnx, this.SetIntersectionCount(i)); } } // Make sure we don't go into an infinite loop. nloop++; if (nloop > m_Links.Length) { throw new Exception("PolygonSub: Breaking from infinite loop"); } } // end while }