public static bool HaveSameVertices( WKSPointZ[] coords1, WKSPointZ[] coords2, double xyTolerance, double zTolerance, bool ignoreDuplicateVertices = true) { Assert.ArgumentNotNull(coords1, nameof(coords1)); Assert.ArgumentNotNull(coords2, nameof(coords2)); IComparer <WKSPointZ> comparer = new WKSPointZComparer(); Array.Sort(coords1, comparer); Array.Sort(coords2, comparer); var i1 = 0; var i2 = 0; while (i1 < coords1.Length && i2 < coords2.Length) { WKSPointZ a = coords1[i1]; WKSPointZ b = coords2[i2]; if (!GeometryUtils.IsSamePoint(a, b, xyTolerance, zTolerance)) { return(false); } // Advance to next set of coordinates: ++i1; ++i2; // Skip identical points in sequence! while (ignoreDuplicateVertices && ArePointsEqual(coords1, i1, i1 - 1, xyTolerance, zTolerance)) { ++i1; } while (ignoreDuplicateVertices && ArePointsEqual(coords2, i2, i2 - 1, xyTolerance, zTolerance)) { ++i2; } } return((i1 == coords1.Length) && (i2 == coords2.Length)); }
private Dictionary <WKSPointZ, VertexIndex> CreateCoordinateDictionary( [NotNull] IGeometry geometry, bool compare3D, out Dictionary <WKSPointZ, List <VertexIndex> > duplicateCoordinates) { double zTolerance = compare3D ? _zTolerance : double.NaN; var comparer = new WKSPointZComparer(_xyTolerance, zTolerance, geometry.SpatialReference); var coordinateDictionary = new Dictionary <WKSPointZ, VertexIndex>(((IPointCollection)geometry).PointCount, comparer); WKSPointZ[] pointArray = GeometryUtils.GetWKSPointZs(geometry, true); // NOTE: there can be ambiguity if two different segments have the same point (rings, boundary loops) // or in the 2D case if two points differ only in Z. Hence duplicates must be explicitly managed. var currentPartIdx = 0; var currentIndexInPart = 0; var geometryCollection = geometry as IGeometryCollection; int partCount = geometryCollection?.GeometryCount ?? 1; duplicateCoordinates = new Dictionary <WKSPointZ, List <VertexIndex> >(partCount); // one duplicate for each ring IPointCollection currentPartPoints = geometryCollection == null ? (IPointCollection)geometry : null; foreach (WKSPointZ wksPointZ in pointArray) { if (currentPartPoints == null && geometryCollection != null) { currentPartPoints = geometryCollection.get_Geometry(currentPartIdx) as IPointCollection; } bool isLastInPart = currentPartPoints != null && currentIndexInPart == currentPartPoints.PointCount - 1; var currentVertexIndex = new VertexIndex(currentPartIdx, currentIndexInPart, isLastInPart); if (!coordinateDictionary.ContainsKey(wksPointZ)) { coordinateDictionary.Add(wksPointZ, currentVertexIndex); } else if (!compare3D && !double.IsNaN(_zTolerance)) { VertexIndex alreadyAdded = coordinateDictionary[wksPointZ]; List <VertexIndex> duplicateIndices; if (!duplicateCoordinates.TryGetValue(wksPointZ, out duplicateIndices)) { duplicateIndices = new List <VertexIndex>(2) { alreadyAdded }; duplicateCoordinates.Add(wksPointZ, duplicateIndices); } duplicateIndices.Add(currentVertexIndex); } if (isLastInPart) { currentPartIdx++; currentPartPoints = null; currentIndexInPart = 0; } else { currentIndexInPart++; } } return(coordinateDictionary); }
private IList <WKSPointZ> GetChangedVertices( WKSPointZ[] baseCoords, WKSPointZ[] compareCoords, bool reportDuplicateVertices, bool baseInsertsOnly) { Assert.ArgumentNotNull(baseCoords, nameof(baseCoords)); Assert.ArgumentNotNull(compareCoords, nameof(compareCoords)); var comparer = new WKSPointZComparer(_xyTolerance, _zTolerance, 0, 0, 0); Array.Sort(baseCoords, comparer); Array.Sort(compareCoords, comparer); var result = new List <WKSPointZ>(); var i1 = 0; var i2 = 0; while (i1 < baseCoords.Length && i2 < compareCoords.Length) { WKSPointZ a = baseCoords[i1]; WKSPointZ b = compareCoords[i2]; bool inSync = GeometryUtils.IsSamePoint(a, b, _xyTolerance, _zTolerance); if (!inSync) { // a is greater than b, advance b until in sync with a bool matchAdvanceCompareCoords = AdvanceIndexUntilMatch( compareCoords, ref i2, a, comparer, result, !baseInsertsOnly, reportDuplicateVertices); // b is greater than a, advance a until in sync with b bool matchAdvanceBaseCoords = AdvanceIndexUntilMatch( baseCoords, ref i1, b, comparer, result, true, reportDuplicateVertices); if (matchAdvanceCompareCoords || matchAdvanceBaseCoords) { inSync = true; } } // Advance to next set of coordinates only if in sync to make sure no difference points are missed if (inSync) { ++i1; ++i2; } // Skip identical points in sequence otherwise From/To points in rotated ring get reported as changes // NOTE: SameCoords tolerates index out of bounds! while (WKSPointZUtils.ArePointsEqual(baseCoords, i1, i1 - 1, _xyTolerance, _zTolerance)) { bool compareCoordsHasDuplicateToo = inSync && WKSPointZUtils.ArePointsEqual(compareCoords, i2, i2 - 1, _xyTolerance, _zTolerance); if (compareCoordsHasDuplicateToo) { // in sync and both have a duplicate at the same location -> unchanged, do not report // but increment both indexes ++i2; } else { // not in sync or compare geometry has no duplicate -> changed, report on request if (reportDuplicateVertices) { result.Add(baseCoords[i1]); } } // eventually landing at the next non-identical point: ++i1; } // process remaining duplicates at current location (or at original location if not in sync) while (WKSPointZUtils.ArePointsEqual(compareCoords, i2, i2 - 1, _xyTolerance, _zTolerance)) { if (reportDuplicateVertices && !baseInsertsOnly) { result.Add(compareCoords[i2]); } ++i2; } } AddRemainingPoints(baseCoords, compareCoords, i1, i2, result, reportDuplicateVertices, baseInsertsOnly); return(result); }
public static IList <KeyValuePair <WKSPointZ, List <WKSPointZ> > > GroupPoints( [NotNull] WKSPointZ[] coords, double xyTolerance, double zTolerance) { Assert.ArgumentNotNull(coords, nameof(coords)); IComparer <WKSPointZ> comparer = new WKSPointZComparer(); Array.Sort(coords, comparer); var toleranceGroups = new List <List <WKSPointZ> >(); var currentGroup = new List <WKSPointZ> { coords[0] }; for (var i = 1; i < coords.Length; i++) { if (!ArePointsEqual(coords, i - 1, i, xyTolerance, zTolerance)) { toleranceGroups.Add(currentGroup); currentGroup = new List <WKSPointZ> { coords[i] }; continue; } currentGroup.Add(coords[i]); } if (currentGroup.Count > 0) { toleranceGroups.Add(currentGroup); } var result = new List <KeyValuePair <WKSPointZ, List <WKSPointZ> > >(); // For strict interpretation of tolerance: Consider splitting clusters that are too large (Divisive Hierarchical clustering) foreach (List <WKSPointZ> groupedPoints in toleranceGroups) { if (groupedPoints.Count == 1) { result.Add(new KeyValuePair <WKSPointZ, List <WKSPointZ> >(groupedPoints[0], groupedPoints)); } else { var center = new WKSPointZ { X = groupedPoints.Average(p => p.X), Y = groupedPoints.Average(p => p.Y), Z = groupedPoints.Average(p => p.Z) }; result.Add(new KeyValuePair <WKSPointZ, List <WKSPointZ> >(center, groupedPoints)); } } return(result); }