private IBox CreateSearchBox([NotNull] IPnt point) { double e = SearchDistance; return(GeomUtils.CreateBox(point.X - e, point.Y - e, point.X + e, point.Y + e)); }
private static void GetClosePart([NotNull] SegmentProxy neighbor, [NotNull] IPnt near, double searchDistanceSquared, bool is3D, out double min, out double max) { IList <double[]> limits; Pnt p = Pnt.Create(near); bool cut = SegmentUtils.CutCurveCircle(neighbor, p, searchDistanceSquared, is3D, out limits); if (cut == false || limits.Count == 0) { min = SegmentUtils.GetClosestPointFraction(neighbor, p, is3D); max = min; } else { min = double.MaxValue; max = double.MinValue; foreach (double[] limit in limits) { foreach (double d in limit) { min = Math.Min(d, min); max = Math.Max(d, max); } } } min = Math.Max(min, 0); max = Math.Max(max, 0); min = Math.Min(min, 1); max = Math.Min(max, 1); }
private int ReportZDiffersCoincidentEdge( [NotNull] IPnt point, double zTolerance, double deltaZ, [NotNull] ISpatialReference spatialReference, params IFeature[] features) { bool sameFeature = features.Length == 1; string format = sameFeature ? LocalizableStrings .VertexCoincidenceChecker_ZDifference_CoincidentEdge_SameFeature : LocalizableStrings .VertexCoincidenceChecker_ZDifference_CoincidentEdge_DifferentFeature; string code = sameFeature ? Code.ZDifference_CoincidentEdge_SameFeature : Code.ZDifference_CoincidentEdge_DifferentFeature; string description = string.Format(format, _formatComparisonFunction(Math.Abs(deltaZ), ">", zTolerance, "N2")); return(_errorReporting.Report(description, CreateErrorGeometry(point, spatialReference), Codes[code], GetAffectedComponent(features), new object[] { Math.Abs(deltaZ) }, features.Cast <IRow>().ToArray())); }
private int InitPlaneOffsets(bool reportErrors, [CanBeNull] IFeature involvedFeature) { const int noError = 0; if (_minOffset <= 0 && _maxOffset >= 0) { return(noError); } Plane3D plane = Plane; var unitNormal = UnitNormal; // double nf = plane.Nf; _minOffset = 0; _maxOffset = 0; var segmentsCount = 0; foreach (SegmentProxy segment in SegmentsPlane.Segments) { segmentsCount++; IPnt point = segment.GetStart(true); // double f = normal.X * point.X + normal.Y * point.Y + normal.Z * point[2] + nf; double distanceSigned = plane.GetDistanceSigned(point.X, point.Y, point[2]); double offset = Math.Abs(distanceSigned); if (distanceSigned > 0 == unitNormal[2] > 0 ) // oriented same as normal { _maxOffset = Math.Max(offset, _maxOffset); } else { _minOffset = Math.Min(-offset, _minOffset); } } var coplanarityTolerance = GeomUtils.AdjustCoplanarityTolerance( plane, _parent.CoplanarityTolerance, _parent._zSrTolerance, _parent._xySrTolerance); double maxOffset = Math.Max(Math.Abs(_maxOffset), Math.Abs(_minOffset)); _coplanar = maxOffset < coplanarityTolerance; if (_coplanar || !reportErrors || involvedFeature == null) { return(noError); } IMultiPatch errorGeometry = SegmentUtils.CreateMultiPatch(SegmentsPlane.Segments); return(_parent.ReportNonCoplanarFace(segmentsCount, maxOffset, errorGeometry, involvedFeature)); }
private static IPoint CreateErrorGeometry( [NotNull] IPnt point, [NotNull] ISpatialReference spatialReference) { IPoint result = new PointClass(); result.PutCoords(point.X, point.Y); result.Z = point[2]; result.SpatialReference = spatialReference; return(result); }
public double GetPointDistance([NotNull] IPnt vertex) { Pnt difference = Point - vertex; double distance2D; if (MathUtils.AreSignificantDigitsEqual(Point.X, vertex.X)) { distance2D = MathUtils.AreSignificantDigitsEqual(Point.Y, vertex.Y) ? 0 : Math.Abs(difference.Y); } else if (MathUtils.AreSignificantDigitsEqual(Point.Y, vertex.Y)) { distance2D = MathUtils.AreSignificantDigitsEqual(Point.X, vertex.X) ? 0 : Math.Abs(difference.X); } else { // both differences are significant double distanceSquared = difference.X * difference.X + difference.Y * difference.Y; if (As3D) { if (!MathUtils.AreSignificantDigitsEqual(Point[2], vertex[2])) { distanceSquared += difference[2] * difference[2]; } } return(Math.Sqrt(distanceSquared)); } if (As3D) { double distanceSquared = distance2D * distance2D; if (!MathUtils.AreSignificantDigitsEqual(Point[2], vertex[2])) { distanceSquared += difference[2] * difference[2]; } return(Math.Sqrt(distanceSquared)); } return(distance2D); }
public override IPnt GetNearestEdgePoint(out bool isVertex) { double fraction = Fraction; if (fraction < 0 || fraction > 1) { isVertex = true; return(GetNearestVertex()); } isVertex = fraction <= 0 || fraction >= 1; IPnt edgePoint = _segmentProxy.GetPointAt(fraction, as3D: true); return(edgePoint); }
private bool IsCoincident([NotNull] PlaneHelper planeHelper, [NotNull] Pnt point, double xyTolerance) { double near = Math.Max(xyTolerance, PointCoincidence); double near2 = near * near; double nearEdge2 = 0; if (EdgeCoincidence > 0) { double nearEdge = Math.Max(xyTolerance, EdgeCoincidence); nearEdge2 = nearEdge * nearEdge; } const bool as3D = true; foreach (SegmentProxy segment in planeHelper.GetSegments()) { if (segment.GetStart(as3D).Dist2(point) < near2) { return(true); } if (EdgeCoincidence > 0) { double fraction = SegmentUtils.GetClosestPointFraction(segment, point, as3D); if (fraction >= 0 && fraction <= 1) { IPnt edgePoint = segment.GetPointAt(fraction, as3D); if (point.Dist2(edgePoint) < nearEdge2) { return(true); } } } } return(false); }
private static void GetClosePart([NotNull] SegmentProxy neighbor, [NotNull] IIndexedSegments geom, [NotNull] SegmentPart segPart, double searchDistanceSquared, bool is3D, out double min, out double max) { SegmentProxy part = geom.GetSegment(segPart.PartIndex, segPart.SegmentIndex); IPnt start = part.GetPointAt(segPart.MinFraction); double minStart, maxStart; GetClosePart(neighbor, start, searchDistanceSquared, is3D, out minStart, out maxStart); IPnt end = part.GetPointAt(segPart.MaxFraction); double minEnd, maxEnd; GetClosePart(neighbor, end, searchDistanceSquared, is3D, out minEnd, out maxEnd); min = Math.Min(minStart, minEnd); max = Math.Max(maxStart, maxEnd); }
private int ReportNearbyEdgeNotPassingThroughVertex( [NotNull] IPnt point, double edgeTolerance, double edgeDistance, [NotNull] ISpatialReference spatialReference, params IFeature[] features) { bool sameFeature = features.Length == 1; string format = sameFeature ? LocalizableStrings .VertexCoincidenceChecker_NearbyEdgeNotPassingThroughVertex_SameFeature : LocalizableStrings .VertexCoincidenceChecker_NearbyEdgeNotPassingThroughVertex_DifferentFeature; string code = sameFeature ? Code.NearbyEdgeNotPassingThroughVertex_SameFeature : Code.NearbyEdgeNotPassingThroughVertex_DifferentFeature; string description = string.Format(format, FormatComparison(edgeDistance, edgeTolerance)); if (ReportCoordinates) { description = description + string.Format( LocalizableStrings .VertexCoincidenceChecker_NearbyEdgeNotPassingThroughVertex_ReportCoordinatesSuffix, point.X, point.Y); } return(_errorReporting.Report(description, CreateErrorGeometry(point, spatialReference), Codes[code], GetAffectedComponent(features), new object[] { edgeDistance }, features.Cast <IRow>().ToArray())); }
private int CheckCoincidenceSameFeature( [NotNull] IPnt point, [NotNull] IFeature feature, [NotNull] IEnumerable <Proximity> proximities, double pointTolerance, double edgeTolerance, [NotNull] ISpatialReference spatialReference) { double closestEdgeDistance = double.MaxValue; double closestEdgeDeltaZ = 0; double?zCoincidenceTolerance = null; foreach (Proximity proximity in proximities) { if (pointTolerance > 0) { IPnt nearestVertex = proximity.GetNearestVertex(); double vertexDistance = proximity.GetPointDistance(nearestVertex); if (vertexDistance < pointTolerance) { if (Math.Abs(vertexDistance) < double.Epsilon) { // probably the same vertex (can't be sure though!) } else if (vertexDistance > CoincidenceTolerance) { // not coincident return(ReportNearbyVertexNotCoincident(point, nearestVertex, pointTolerance, vertexDistance, spatialReference, feature)); } if (CheckZ) { double deltaZ = nearestVertex[2] - proximity.Point[2]; zCoincidenceTolerance = zCoincidenceTolerance ?? GetZCoincidenceTolerance(new[] { feature }); if (IsInvalidDeltaZ(deltaZ, zCoincidenceTolerance.Value)) { return(ReportZDiffersCoincidentVertex( point, zCoincidenceTolerance.Value, deltaZ, spatialReference, feature)); } } } } if (edgeTolerance > 0) { bool isVertex; IPnt nearestEdgePoint = proximity.GetNearestEdgePoint(out isVertex); double edgeDistance = proximity.GetPointDistance(nearestEdgePoint); if (edgeDistance < edgeTolerance) { if (!isVertex && edgeDistance < closestEdgeDistance) { closestEdgeDistance = edgeDistance; if (CheckZ) { closestEdgeDeltaZ = nearestEdgePoint[2] - proximity.Point[2]; } } } } } if (closestEdgeDistance < edgeTolerance) { // there is a nearby edge within the edge tolerance if (RequireVertexOnNearbyEdge) { // and that is never allowed (without a coincident vertex) return(ReportNoVertexOnNearbyEdge(point, edgeTolerance, closestEdgeDistance, spatialReference, feature)); } if (closestEdgeDistance > CoincidenceTolerance) { // the nearby edge does not pass through the vertex return(ReportNearbyEdgeNotPassingThroughVertex( point, edgeTolerance, closestEdgeDistance, spatialReference, feature)); } if (CheckZ) { zCoincidenceTolerance = zCoincidenceTolerance ?? GetZCoincidenceTolerance(new[] { feature }); if (IsInvalidDeltaZ(closestEdgeDeltaZ, zCoincidenceTolerance.Value)) { return(ReportZDiffersCoincidentEdge(point, zCoincidenceTolerance.Value, closestEdgeDeltaZ, spatialReference, feature)); } } } return(_noError); }
private int CheckCoincidenceDifferentFeatures( [NotNull] IPnt point, [NotNull] IFeature feature, [NotNull] IFeature nearFeature, [NotNull] IEnumerable <Proximity> proximities, double pointTolerance, double edgeTolerance, [NotNull] ISpatialReference spatialReference) { double closestPointDistance = double.MaxValue; double closestEdgeDistance = double.MaxValue; double closestEdgeDeltaZ = 0; double epsilon = MathUtils.GetDoubleSignificanceEpsilon(Math.Max(Math.Abs(point.X), Math.Abs(point.Y))); Pnt closestPoint = null; foreach (Proximity proximity in proximities) { if (pointTolerance > 0) { Pnt nearestVertex = proximity.GetNearestVertex(); double vertexDistance = proximity.GetPointDistance(nearestVertex); if (MathUtils.IsWithinTolerance(vertexDistance, pointTolerance, epsilon)) { closestPointDistance = Math.Min(vertexDistance, closestPointDistance); closestPoint = nearestVertex; if (MathUtils.IsWithinTolerance(closestPointDistance, CoincidenceTolerance, epsilon)) { // there is at least one close enough point on the near feature if (CheckZ) { // ReSharper disable once ConvertToConstant.Local double deltaZ = nearestVertex[2] - proximity.Point[2]; double zCoincidenceTolerance = GetZCoincidenceTolerance(new[] { feature, nearFeature }); // get delta Z (also if is3D = false !!!!) if (IsInvalidDeltaZ(deltaZ, zCoincidenceTolerance)) { return(ReportZDiffersCoincidentVertex( point, zCoincidenceTolerance, deltaZ, spatialReference, feature, nearFeature)); } } return(_noError); } } } if (edgeTolerance > 0) { bool isVertex; IPnt nearestEdgePoint = proximity.GetNearestEdgePoint(out isVertex); double edgeDistance = proximity.GetPointDistance(nearestEdgePoint); if (!isVertex && edgeDistance < closestEdgeDistance) { closestEdgeDistance = edgeDistance; if (CheckZ) { closestEdgeDeltaZ = nearestEdgePoint[2] - proximity.Point[2]; } } } } if (MathUtils.IsWithinTolerance(closestPointDistance, pointTolerance, epsilon) && !MathUtils.IsWithinTolerance(closestPointDistance, CoincidenceTolerance, epsilon)) { Assert.NotNull(closestPoint); return(ReportNearbyVertexNotCoincident(point, closestPoint, pointTolerance, closestPointDistance, spatialReference, feature, nearFeature)); } if (MathUtils.IsWithinTolerance(closestEdgeDistance, edgeTolerance, epsilon)) { // there is a nearby edge within the edge tolerance if (RequireVertexOnNearbyEdge) { // and that is never allowed (without a coincident vertex) // the nearby edge does not have a vertex return(ReportNoVertexOnNearbyEdge(point, edgeTolerance, closestEdgeDistance, spatialReference, feature, nearFeature)); } if (!MathUtils.IsWithinTolerance(closestEdgeDistance, CoincidenceTolerance, epsilon)) { // the nearby edge does not pass through the vertex return(ReportNearbyEdgeNotPassingThroughVertex(point, edgeTolerance, closestEdgeDistance, spatialReference, feature, nearFeature)); } if (CheckZ) { double zCoincidenceTolerance = GetZCoincidenceTolerance(new[] { feature, nearFeature }); if (IsInvalidDeltaZ(closestEdgeDeltaZ, zCoincidenceTolerance)) { return(ReportZDiffersCoincidentEdge(point, zCoincidenceTolerance, closestEdgeDeltaZ, spatialReference, feature, nearFeature)); } } // the closest edge on the near feature is near enough return(_noError); } return(_noError); }
private static double GetDistanceToCurve( [NotNull] IPoint point, [NotNull] IFeature neighbourFeature, [NotNull] IPoint nearestPoint, double maxNeededDistance, out bool onRightSide) { var neighborCurve = (ICurve)neighbourFeature.Shape; if (UseQueryPointAndDistance) { const bool asRatio = false; double along = 0; double distance = 0; var rightSide = false; // TLMQA-292 (EBG - BB): most time spent here (> 90%) neighborCurve.QueryPointAndDistance(esriSegmentExtension.esriNoExtension, point, asRatio, nearestPoint, ref along, ref distance, ref rightSide); onRightSide = rightSide; return(distance); } { double nearestDistance = maxNeededDistance * 1.01; bool? nearestOnRightSide = null; SegmentProxy nearestSegment = null; double nearestFraction = 0; double x; double y; point.QueryCoords(out x, out y); Pnt qaPoint = new Pnt2D(x, y); foreach (SegmentProxy segmentProxy in EnumSegments(qaPoint, neighbourFeature, maxNeededDistance)) { bool? onSegmentRightSide; double alongFraction; double offset = GetOffset(qaPoint, segmentProxy, out alongFraction, out onSegmentRightSide); if (offset <= nearestDistance) { if (!onSegmentRightSide.HasValue && !nearestOnRightSide.HasValue && nearestSegment != null) { nearestOnRightSide = GetOnRightSide( nearestSegment, nearestFraction, segmentProxy, alongFraction, neighborCurve); } else if (offset < nearestDistance) { nearestOnRightSide = onSegmentRightSide; } nearestDistance = offset; nearestSegment = segmentProxy; nearestFraction = alongFraction; } } if (nearestSegment != null) { double f = Math.Min(1, Math.Max(0, nearestFraction)); IPnt p = nearestSegment.GetPointAt(f, as3D: true); nearestPoint.PutCoords(p.X, p.Y); nearestPoint.Z = p[2]; if (!nearestOnRightSide.HasValue) { // Extend segment lineary to determine right side Pnt s = nearestSegment.GetStart(as3D: false); Pnt e = nearestSegment.GetEnd(as3D: false); if (nearestFraction >= 1) { nearestOnRightSide = (e - s).VectorProduct(e - qaPoint) > 0; } else if (nearestFraction <= 0) { nearestOnRightSide = (e - s).VectorProduct(s - qaPoint) > 0; } } } onRightSide = nearestOnRightSide ?? false; return(nearestDistance); } }
protected override int ExecuteCore(IRow row, int tableIndex) { int errorCount = 0; var feature = row as IFeature; if (feature == null) { return(errorCount); } var geometryCollection = feature.Shape as IGeometryCollection; if (geometryCollection == null) { return(errorCount); } SegmentsPlaneProvider segmentsPlaneProvider = GetPlaneProvider(feature); SegmentsPlane segmentsPlane; while ((segmentsPlane = segmentsPlaneProvider.ReadPlane()) != null) { Plane3D plane; try { plane = segmentsPlane.Plane; } catch (Exception e) { errorCount += ReportInvalidPlane( $"Unable to determine plane: {e.Message}", segmentsPlane, feature); continue; } if (!plane.IsDefined) { errorCount += ReportInvalidPlane( "The segments of this face are collinear and do not define a valid plane", segmentsPlane, feature); continue; } double coplanarityTolerance = GeomUtils.AdjustCoplanarityTolerance(plane, _coplanarityTolerance, _zResolution, _xyResolution); double maxOffset = -1; int segmentsCount = 0; foreach (SegmentProxy segment in segmentsPlane.Segments) { segmentsCount++; IPnt point = segment.GetStart(true); //double f = normal.X * point.X + normal.Y * point.Y + // normalZ * point[2] + nf; // double offset = Math.Abs(f); double offset = plane.GetDistanceAbs(point.X, point.Y, point[2]); if (offset <= coplanarityTolerance) { continue; } maxOffset = Math.Max(offset, maxOffset); } if (segmentsCount < 3) { // TODO no need to check here once the plane is undefined in this case --> REMOVE const string description = "The segments of this face are collinear and do not define a valid plane"; IGeometry errorGeometry = GetErrorGeometry(feature.Shape.GeometryType, segmentsPlane.Segments); errorCount += ReportError(description, errorGeometry, Codes[Code.FaceDoesNotDefineValidPlane], null, row); continue; } if (maxOffset > 0) { string comparison = FormatLengthComparison( maxOffset, ">", _coplanarityTolerance, feature.Shape.SpatialReference); string description = $"Face with {segmentsCount} segments is not planar, max. offset = {comparison}"; IGeometry errorGeometry = GetErrorGeometry(feature.Shape.GeometryType, segmentsPlane.Segments); errorCount += ReportError(description, errorGeometry, Codes[Code.FaceNotCoplanar], TestUtils.GetShapeFieldName(feature), InvolvedRowUtils.GetInvolvedRows(row), new object[] { maxOffset }); } } return(errorCount); }
public IList <IRow> Search([NotNull] ITable table, int tableIndex, [NotNull] IQueryFilter queryFilter, [NotNull] QueryFilterHelper filterHelper, [CanBeNull] IGeometry cacheGeometry) { var spatialFilter = (ISpatialFilter)queryFilter; IGeometry filterGeometry = spatialFilter.Geometry; IList <IRow> result = new List <IRow>(); // TODO explain network queries bool repeatCachedRows = filterHelper.ForNetwork; // filterHelper.PointSearchOnlyWithinTile if (filterHelper.ForNetwork) { var filterPoint = filterGeometry as IPoint; if (filterPoint != null) { // search only if the point is within the tile box // (or left/below of test run box) double x; double y; filterPoint.QueryCoords(out x, out y); Pnt tileMin = CurrentTileBox.Min; Pnt tileMax = CurrentTileBox.Max; IPnt testRunMin = _testRunBox.Min; if ((x <= tileMin.X && x > testRunMin.X) || x > tileMax.X || (y <= tileMin.Y && y > testRunMin.Y) || y > tileMax.Y) { // outside of tile box, return empty list return(result); } } } List <BoxTree <CachedRow> .TileEntry> searchList = SearchList(filterGeometry, tableIndex); if (searchList == null || searchList.Count == 0) { return(result); } var cacheGeometryOverlapsLeftTile = false; var cacheGeometryOverlapsBottomTile = false; if (!repeatCachedRows) { if (cacheGeometry != null) { cacheGeometry.QueryEnvelope(_envelopeTemplate); } else { filterGeometry.QueryEnvelope(_envelopeTemplate); } double xmin; double ymin; double xmax; double ymax; _envelopeTemplate.QueryCoords(out xmin, out ymin, out xmax, out ymax); cacheGeometryOverlapsLeftTile = xmin <CurrentTileBox.Min.X && xmin> _testRunBox.Min.X; // https://issuetracker02.eggits.net/browse/COM-85 // observed (CtLu): // - filter geometry ymin = 220532.967 // - filter geometry ymax = 220557.78500 // - tile ymin = 220557.78534 // --> filter geometry is completely outside of tile boundaries!!! // --> causes incorrect error in QaContainsOther cacheGeometryOverlapsBottomTile = ymin <CurrentTileBox.Min.Y && ymin> _testRunBox.Min.Y; } IGeometryEngine engine = _container.GeometryEngine; engine.SetSourceGeometry(filterGeometry); IList <ContainerTest> tests = _testsPerTable[table]; int indexTest = tests.IndexOf(filterHelper.ContainerTest); IList <BaseRow> ignoredRows = IgnoredRowsByTableAndTest[tableIndex][indexTest]; foreach (BoxTree <CachedRow> .TileEntry entry in searchList) { CachedRow cachedRow = Assert.NotNull(entry.Value, "cachedRow"); // This causes problems for QaIsCoveredByOther. However // IsCoveredByOther is not a network test, but still requires cached features // to be returned repeatedly if (cacheGeometryOverlapsLeftTile && !cachedRow.IsFirstOccurrenceX) { // only if *not for network*: // the filter geometry overlaps the left border of the tile, but // not the left border of the test run box AND the cached row // was already returned previously --> skip it continue; } if (cacheGeometryOverlapsBottomTile && !cachedRow.IsFirstOccurrenceY) { // only if *not for network*: // the filter geometry overlaps the bottom border of the tile, but // not the bottom border of the test run box AND the cached row // was already returned previously --> skip it continue; } if (ignoredRows != null && ignoredRows.Contains(entry.Value)) { continue; } IFeature targetFeature = cachedRow.Feature; if (targetFeature.OID < filterHelper.MinimumOID) { continue; } engine.SetTargetGeometry(cachedRow.Geometry); // Remark: if most of the rows fullfill helper.Check, // it is better to check the geometric relation first var matchesConstraint = false; if (filterHelper.AttributeFirst) { if (!filterHelper.MatchesConstraint(targetFeature)) { continue; } matchesConstraint = true; } if (engine.EvaluateRelation(spatialFilter)) { if (matchesConstraint || filterHelper.MatchesConstraint(targetFeature)) { result.Add(targetFeature); } } } return(result); }