private void CreateMissingPKError(IFeature theEditIndicator, string[] pks, string editType) { IPoint theErrorPoint = this.get_ErrorPoint(theEditIndicator.Shape); theErrorPoint.Project(SpatialReferenceHelper.GeographicReference); DataQualityError theError = new DataQualityError(this.Name, false, false); theError.Location = theErrorPoint; theError.Severity = 1; if (editType == "insert") theError.Description = "Inserted shape has null primary key information"; else if (editType == "delete") theError.Description = "Deleted feature has null primary key information"; else theError.Description = "Edited shape has null primary key information"; ExtendedInfo theInfo = new ExtendedInfo(); string theSourceDSName = ""; IDataset theSourceDS = (IDataset)theEditIndicator.Table; theSourceDSName = theSourceDS.Name; theInfo.AddProperty("Feature class", theSourceDSName); theInfo.AddProperty("Feature ID: ", theEditIndicator.OID.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); }
private void EvaluateEditInSee( IFeature workingFeature, IFeature pristineFeature, IPolygon seeBounds, string seeID, string pkWhereClause, double shiftLimit) { bool bWorkingFeatureOutside = false; bool bPristineFeatureOutside = false; bool bUpdateOutOfBounds = false; HashSet<IPoint> theOffendingVertices = null; IRelationalOperator theRelOp = (IRelationalOperator)seeBounds; IGeometry theWorkingShape = null; IGeometry thePristineShape = null; try { if (workingFeature != null) { theWorkingShape = this.ShapeCopy(workingFeature); if (theRelOp.Contains(theWorkingShape) == false) { bWorkingFeatureOutside = true; } } if (pristineFeature != null) { thePristineShape = this.ShapeCopy(pristineFeature); if (theRelOp.Contains(thePristineShape) == false) { bPristineFeatureOutside = true; } } bUpdateOutOfBounds = bWorkingFeatureOutside || bPristineFeatureOutside; if (bUpdateOutOfBounds && theWorkingShape != null && thePristineShape != null) { IGeometry theWorkingShapeOutsideSee = ((ITopologicalOperator)theWorkingShape).Difference(seeBounds); IGeometry thePristineShapeOutsideSee = ((ITopologicalOperator)thePristineShape).Difference(seeBounds); if (theWorkingShapeOutsideSee == null || theWorkingShapeOutsideSee.IsEmpty) { bUpdateOutOfBounds = false; } else if (((IRelationalOperator)theWorkingShapeOutsideSee).Equals(thePristineShapeOutsideSee)) { // shape unchanged bUpdateOutOfBounds = false; } else if (!((IRelationalOperator)theWorkingShapeOutsideSee).Equals(thePristineShapeOutsideSee)) { IGeometry pristineClone = pristineFeature.ShapeCopy; IGeometry workingClone = workingFeature.ShapeCopy; string theTxID = this.TxID; IQueryFilter theQF = new QueryFilterClass(); theQF.WhereClause = SEE_ID_FIELD_NAME + " = '" + theTxID + "'"; IFeatureCursor theFCursor = this.SEELayer.Search(theQF, false); IFeature theSee = theFCursor.NextFeature(); Marshal.ReleaseComObject(theFCursor); theFCursor = null; IGeometry seeClone = theSee.ShapeCopy; // simplify the clones (see SEE.cs line 92?) ITopologicalOperator2 simplifyPristineTo = (ITopologicalOperator2)pristineClone; simplifyPristineTo.IsKnownSimple_2 = false; simplifyPristineTo.Simplify(); ITopologicalOperator2 simplifyWorkingTo = (ITopologicalOperator2)workingClone; simplifyWorkingTo.IsKnownSimple_2 = false; simplifyWorkingTo.Simplify(); // clone the SEE. Reproject the SEE to the pristines projection ITopologicalOperator2 simplifySeeTo = (ITopologicalOperator2)seeClone; simplifySeeTo.IsKnownSimple_2 = false; simplifySeeTo.Simplify(); seeClone.Project(pristineClone.SpatialReference); ITopologicalOperator baseWorkingTo = (ITopologicalOperator)workingClone; IGeometry bufferWorkingShape = baseWorkingTo.Buffer(shiftLimit); ITopologicalOperator pristineTo = (ITopologicalOperator)pristineClone; IGeometry diffedGeom = pristineTo.Difference(bufferWorkingShape); diffedGeom.SpatialReference = workingClone.SpatialReference; ITopologicalOperator basePristineTo = (ITopologicalOperator)pristineClone; IGeometry bufferPristineShape = basePristineTo.Buffer(shiftLimit); ITopologicalOperator workingTo = (ITopologicalOperator)workingClone; IGeometry secondDiffedGeom = workingTo.Difference(bufferPristineShape); secondDiffedGeom.SpatialReference = pristineClone.SpatialReference; theOffendingVertices = new HashSet<IPoint>(); GetOffendingVertices(diffedGeom, seeClone, theOffendingVertices); GetOffendingVertices(secondDiffedGeom, seeClone, theOffendingVertices); if(theOffendingVertices.Count == 0) bUpdateOutOfBounds = false; while (Marshal.ReleaseComObject(baseWorkingTo) > 0) { } while (Marshal.ReleaseComObject(bufferWorkingShape) > 0) { } while (Marshal.ReleaseComObject(pristineTo) > 0) { } while (Marshal.ReleaseComObject(diffedGeom) > 0) { } while (Marshal.ReleaseComObject(basePristineTo) > 0) { } while (Marshal.ReleaseComObject(bufferPristineShape) > 0) { } while (Marshal.ReleaseComObject(workingTo) > 0) { } while (Marshal.ReleaseComObject(secondDiffedGeom) > 0) { } while (Marshal.ReleaseComObject(pristineClone) > 0) { } while (Marshal.ReleaseComObject(workingClone) > 0) { } diffedGeom = null; pristineTo = null; bufferWorkingShape = null; baseWorkingTo = null; secondDiffedGeom = null; workingTo = null; bufferPristineShape = null; basePristineTo = null; pristineClone = null; workingClone = null; } else { // TODO: This index is causing errors on rebuild geometries using // TODO: topology tools. Verify vertext and shape match, not using an index // Evaluate if this is a real edit or just vertex shifting // Build point collection index for the pristine shape // cellsize: average of thePristineShapeOutsideSee width and height divided by 10 PointCollectionIndex theIndex = new PointCollectionIndex(thePristineShapeOutsideSee.Envelope.Width + thePristineShapeOutsideSee.Envelope.Height / 2 / 10); IPointCollection thePtColl = (IPointCollection)thePristineShapeOutsideSee; for (int i = 0; i < thePtColl.PointCount; i++) theIndex.AddPoint(new IndexPoint(pristineFeature.OID, i, thePtColl.get_Point(i))); // Check each working vertex to see if there is a vertex within the shift limit // If not, this vertex needs flagging theOffendingVertices = new HashSet<IPoint>(); thePtColl = (IPointCollection)theWorkingShapeOutsideSee; for (int i = 0; i < thePtColl.PointCount; i++) { IPoint theWorkingPoint = thePtColl.get_Point(i); IndexPoint thePristinePoint = theIndex.get_ClosestPointWithinLimits(theWorkingPoint.X, theWorkingPoint.Y, -1, shiftLimit); if (thePristinePoint == null) { theOffendingVertices.Add(this.get_ErrorPoint(theWorkingPoint)); } } if (theOffendingVertices.Count == 0) bUpdateOutOfBounds = false; } while (Marshal.ReleaseComObject(theWorkingShapeOutsideSee) > 0){} while (Marshal.ReleaseComObject(thePristineShapeOutsideSee) > 0) { } theWorkingShapeOutsideSee = null; thePristineShapeOutsideSee = null; } } catch (Exception ex) { throw ex; } if (bUpdateOutOfBounds) { // This needs to be flagged. The only exceptions are: // - a spatial update where the difference between the working and pristine shapes falls within the SEE // - an attribute-only update if (theOffendingVertices == null) { theOffendingVertices = new HashSet<IPoint>(); if (theWorkingShape != null) theOffendingVertices.Add(this.get_ErrorPoint(theWorkingShape)); else theOffendingVertices.Add(this.get_ErrorPoint(thePristineShape)); } foreach (IPoint theErrorPoint in theOffendingVertices) { theErrorPoint.Project(SpatialReferenceHelper.GeographicReference); DataQualityError theError = new DataQualityError(this.Name, false, false); theError.Location = theErrorPoint; theError.Severity = 1; if (thePristineShape == null) theError.Description = "Inserted shape extends outside of the SEE area"; else if (theWorkingShape == null) theError.Description = "Deleted feature extends outside of the SEE area"; else theError.Description = "Edited shape extends outside of the SEE area"; ExtendedInfo theInfo = new ExtendedInfo(); string theSourceDSName = ""; if (workingFeature != null) { IDataset theSourceDS = (IDataset)workingFeature.Table; theSourceDSName = theSourceDS.Name; } else { IDataset theSourceDS = (IDataset)pristineFeature.Table; theSourceDSName = "P_" + theSourceDS.Name; } theInfo.AddProperty("Feature class", theSourceDSName); theInfo.AddProperty("Business ID: ", pkWhereClause); if (workingFeature != null) theInfo.AddProperty("Working Feature ID", workingFeature.OID.ToString()); theInfo.AddProperty("SEE ID", seeID.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); } } }
private int AnalyzePair(IFeatureClass fcWork, IFeatureClass fcOsdb, string fieldName, string fkey, int idxWork, int idxOsdb, int idxFKey, bool canDefer, bool canExcept) { // First check that the working fc is self-consistent int errCount = AnalyzeSingle(fcWork, idxWork, canDefer, canExcept); if (errCount != 0) return errCount; // -1 if error, > 0 is a count of errors /* * We know that all of the values in the working layer are unique within * the working layer. Now we need to determine if any working values * line up with multiple osdb records */ IDataset dsWork = (IDataset)fcWork; IDataset dsOsdb = (IDataset)fcOsdb; IQueryDef theQDef = ((IFeatureWorkspace)dsOsdb.Workspace).CreateQueryDef(); theQDef.Tables = dsOsdb.Name; theQDef.SubFields = "count(*)"; IField theField = fcWork.Fields.get_Field(idxWork); string q = ""; if (theField.Type == esriFieldType.esriFieldTypeString) q = "'"; else if (theField.Type == esriFieldType.esriFieldTypeBlob || theField.Type == esriFieldType.esriFieldTypeGeometry || theField.Type == esriFieldType.esriFieldTypeRaster) { this.LogMessage("Fields of type " + theField.Type + " can't be evaluated by " + this.Name); return -1; } IFeatureCursor checkCursor = null; ICursor theCountCursor = null; try { checkCursor = fcWork.Search(null, true); IFeature theFeature = checkCursor.NextFeature(); while (theFeature != null) { theQDef.WhereClause = fieldName + "=" + q + Convert.ToString(theFeature.get_Value(idxWork)) + q + " and " + fkey + "<>" + q + Convert.ToString(theFeature.get_Value(idxFKey)) + q; theCountCursor = theQDef.Evaluate(); IRow theCountRow = theCountCursor.NextRow(); int theCount = Convert.ToInt32(theCountRow.get_Value(0)); Marshal.ReleaseComObject(theCountCursor); theCountCursor = null; if (theCount > 0) { // Error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Non-unique value in unique-constrained field"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Working feature class", dsWork.Name); theInfo.AddProperty("OSDB feature class", dsOsdb.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); object theValue = theFeature.get_Value(idxWork); theInfo.AddProperty("Value", theValue.ToString()); object theFKey = theFeature.get_Value(idxFKey); theInfo.AddProperty("FKey", theFKey.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); errCount++; } theFeature = checkCursor.NextFeature(); } } catch (Exception ex) { this.LogMessage("Error looking for OSDB records:" + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace); return -1; } finally { if (checkCursor != null) Marshal.ReleaseComObject(checkCursor); if (theCountCursor != null) Marshal.ReleaseComObject(theCountCursor); } return errCount; }
private int AnalyzeSingle(IFeatureClass fc, int fieldIdx, bool canDefer, bool canExcept) { int errCount = 0; Hashtable theLookup = new Hashtable(); IDataset theDataset = (IDataset)fc; IFeatureCursor checkCursor = fc.Search(null, true); try { IFeature theFeature = checkCursor.NextFeature(); while (theFeature != null) { object theValue = theFeature.get_Value(fieldIdx); if (theValue == null || Convert.IsDBNull(theValue)) { // Error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Null value in unique-constrained field"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDataset.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); errCount++; } else if (theLookup.ContainsKey(theValue)) { // Error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Non-unique value in unique-constrained field"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDataset.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); theInfo.AddProperty("Value", theValue.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); errCount++; } else { theLookup.Add(theValue, theValue); } theFeature = checkCursor.NextFeature(); } } catch (Exception ex) { this.LogMessage("Error checking unique values:" + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace); return -1; } finally { Marshal.ReleaseComObject(checkCursor); } return errCount; }
private ArrayList ProcessFeature(IFeature test, ISpatialReference referenceSR, double precision) { if (test == null || test.Shape == null || test.Shape.IsEmpty) return null; ArrayList theReturn = new ArrayList(); string polygonError = ""; // test feb 21 int index = this.FindParameter("minimum-arc-proportional"); double proportion = Math.Abs((double)((ParameterInfo)this._params[index]).ParamValue); index = this.FindParameter(ParameterInfo.PARAM_CANDEFER); bool canDefer = (bool)((ParameterInfo)this._params[index]).ParamValue; index = this.FindParameter(ParameterInfo.PARAM_CANEXCEPT); bool canExcept = (bool)((ParameterInfo)this._params[index]).ParamValue; IGeometry theGeometry = test.ShapeCopy; theGeometry.Project(referenceSR); ISegmentCollection theGeomColl = (ISegmentCollection)test.Shape; for (int i = 0; i < theGeomColl.SegmentCount; i++) { IGeometry theGeometryPart = theGeomColl.get_Segment(i); ISegment theCurve = (ISegment)theGeomColl.get_Segment(i); if (theCurve.Length < precision * proportion) { IPoint theErrorPoint = theCurve.FromPoint; theErrorPoint.Project(SpatialReferenceHelper.GeographicReference); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; if (theCurve.Length == 0) theError.Severity = 1; else if (theCurve.Length <= precision) theError.Severity = 2; else theError.Severity = 3; theError.Description = "Arc too short"; // Feb 15, 2008 Testing System.Diagnostics.Debug.WriteLine(string.Format("({0},{1}) - ({2},{3}) {4} {5}", theCurve.FromPoint.X.ToString(), theCurve.FromPoint.Y.ToString(), theCurve.ToPoint.X.ToString(), theCurve.ToPoint.Y.ToString(), theCurve.IsClosed.ToString(), theCurve.Length.ToString())); ExtendedInfo theInfo = new ExtendedInfo(); IDataset theDataset = (IDataset)test.Table; theInfo.AddProperty("Feature class", theDataset.Name); theInfo.AddProperty("Feature ID", test.OID.ToString()); theInfo.AddProperty("Arc length", theCurve.Length.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); theReturn.Add(theError); } } // If a polygon check the geometry type using IsSimpleEx if (test.ShapeCopy.GeometryType == esriGeometryType.esriGeometryPolygon) { IPoint theErrorPoint = this.get_ErrorPoint(theGeometry); theErrorPoint.Project(SpatialReferenceHelper.GeographicReference); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; ITopologicalOperator4 pTopo4 = test.ShapeCopy as ITopologicalOperator4; esriNonSimpleReasonEnum myReason = esriNonSimpleReasonEnum.esriNonSimpleOK; // Initialize myReason pTopo4.IsKnownSimple_2 = false; bool isSimple = pTopo4.get_IsSimpleEx(out myReason); if (!isSimple) { switch (myReason.ToString()) { case "esriNonSimpleOK": theError.Description = "The geometry is simple."; break; case "esriNonSimpleRingOrientation": theError.Description = "A polygon is topologically simple, but its rings may not be oriented correctly (outer rings - cw, inner rings - ccw)."; break; case "esriNonSimpleSegmentOrientation": theError.Description = "Individual segments are not consistantly oriented. The 'to' point of seg i should be incident on the 'from' point of seg i+1."; break; case "esriNonSimpleShortSegments": theError.Description = "Some segments are shorter than allowed by the system units of the spatial reference associated with the geometry."; break; case "esriNonSimpleSelfIntersections": theError.Description = "The interior of each part (rings, planar paths) must not intersect itself or other parts."; break; case "esriNonSimpleUnclosedRing": theError.Description = "The last segment in a ring must have its 'to' point incident on the 'from' point of the first segment."; break; case "esriNonSimpleEmptyPart": theError.Description = "The geometry contains an empty part."; break; default: theError.Description = "Do not understand problem definition code: " + myReason.ToString(); break; } theReturn.Add(theError); } } return theReturn; }
public override int Execute(string logfileName) { this.InitLogging(logfileName); this.LogMessage(this.Name + " QA test execution started."); try { this.LogMessage(this.Name + " Parameters:"); for (int i = 0; i < this.ParameterCount; i++) this.LogMessage(this.get_ParameterText(i) + ": " + this.get_ParameterValue(i)); this.ClearErrors(); bool bProblemsRunning = false; int index = this.FindParameter(ParameterInfo.PARAM_CANDEFER); bool canDefer = (bool)((ParameterInfo)this._params[index]).ParamValue; index = this.FindParameter(ParameterInfo.PARAM_CANEXCEPT); bool canExcept = (bool)((ParameterInfo)this._params[index]).ParamValue; // Compare the shape type of the odd layers with the evens. // If the user has used the magic wand tool, this should be right for (int layerIdx = 0; layerIdx < this.LayerCount; layerIdx+=2) { IFeatureLayer theFLayer1 = this.get_Layer(layerIdx); IFeatureLayer theFLayer2 = this.get_Layer(layerIdx + 1); if (theFLayer1 == null || theFLayer2 == null || theFLayer1.Valid == false || theFLayer2.Valid == false) { bProblemsRunning = true; break; } IFeatureClass theFClass1 = theFLayer1.FeatureClass; IFeatureClass theFClass2 = theFLayer2.FeatureClass; IDataset theDataset1 = (IDataset)theFClass1; IDataset theDataset2 = (IDataset)theFClass2; this.LogMessage("Comparing the shape type of featureclass " + theDataset1.Name + " vs. " + theDataset2.Name); if (theFClass1.ShapeType != theFClass2.ShapeType) { IPoint theErrorPoint = ((IArea)theFLayer1.AreaOfInterest).Centroid; theErrorPoint.Project(SpatialReferenceHelper.GeographicReference); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Wrong shape type"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class 1", theDataset1.Name); theInfo.AddProperty("Feature class 2", theDataset2.Name); theInfo.AddProperty("Shape type 1", theFClass1.ShapeType.ToString()); theInfo.AddProperty("Shape type 2", theFClass2.ShapeType.ToString()); theError.ExtendedData = theInfo.WriteXML(); this._errors.Add(theError); } } this.LogMessage("Number of errors found: " + this.ErrorCount); if (bProblemsRunning) return -1; else this.LogMessage("Test " + this.Name + " successful."); } catch (Exception ex) { this.LogMessage("Exception caught: \n" + ex.Message + "\n" + ex.StackTrace.ToString()); return -1; } finally { this.StopLogging(); } return this.ErrorCount; }
private void CompareSIDs(IFeatureClass workingFeatureClass, IFeatureCursor checkCursor, ITable osdbBizTable, string sidFieldName, bool canDefer, bool canExcept) { IDataset theDS1 = (IDataset)osdbBizTable; IDataset theDS2 = (IDataset)workingFeatureClass; IRelationshipClass theRC = null; try { // Create relationshipclass backwards to normal, b/c we're checking // that the SIDs that exist in the FC exist in the biz table IMemoryRelationshipClassFactory theFactory = new MemoryRelationshipClassFactoryClass(); theRC = theFactory.Open( theDS2.Name + "_to_" + theDS1.Name, (IObjectClass)workingFeatureClass, sidFieldName, (IObjectClass)osdbBizTable, sidFieldName, "forward", "backward", esriRelCardinality.esriRelCardinalityOneToOne); } catch (Exception ex) { this.LogMessage("Error creating memory relationship class between " + theDS2.Name + " and " + theDS1.Name + ":" +Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace); return; } try { int sidIndex = workingFeatureClass.FindField(sidFieldName); IFeature theFeature = checkCursor.NextFeature(); while (theFeature != null) { ISet theSet = theRC.GetObjectsRelatedToObject((IObject)theFeature); if (theSet.Count == 0) { // Error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "No corresponding OSDB record"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDS2.Name); theInfo.AddProperty("OSDB table", theDS1.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); object sidValue = theFeature.get_Value(sidIndex); theInfo.AddProperty("SID value", sidValue == null ? "NULL" : sidValue.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); } theFeature = checkCursor.NextFeature(); } } catch (Exception ex) { this.LogMessage("Error looking for OSDB records:" + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace); } }
private void CompareAreas(IFeatureClass workingFeatureClass, IFeatureCursor checkCursor, ITable relatedTable, string relateFieldName, string areaFieldName, double conversionFactor, double tolerance, bool canDefer, bool canExcept) { IDataset theDS1 = (IDataset)relatedTable; IDataset theDS2 = (IDataset)workingFeatureClass; IRelationshipClass theRC = null; if (relatedTable != null && relateFieldName != null && relateFieldName.Length > 0) { try { // Create relationshipclass backwards to normal, b/c we're checking // that the SIDs that exist in the FC exist in the biz table IMemoryRelationshipClassFactory theFactory = new MemoryRelationshipClassFactoryClass(); IObjectClass bob = (IObjectClass)workingFeatureClass; IObjectClass sam = (IObjectClass)relatedTable; theRC = theFactory.Open( theDS2.Name + "_to_" + theDS1.Name, bob, relateFieldName, sam, relateFieldName, "forward", "backward", esriRelCardinality.esriRelCardinalityOneToOne); } catch (Exception ex) { this.LogMessage("Error creating memory relationship class between " + theDS2.Name + " and " + theDS1.Name + ":" +Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace); return; } } try { int areaFieldIndex = -1; int fkIndex = -1; if (theRC == null) { // Area field is on the feature areaFieldIndex = workingFeatureClass.FindField(areaFieldName); } else { // Area field is on the related table areaFieldIndex = relatedTable.FindField(areaFieldName); fkIndex = workingFeatureClass.FindField(relateFieldName); } IFeature theFeature = checkCursor.NextFeature(); while (theFeature != null) { double areaDbValue = double.NaN; double areaGeoValue = double.NaN; // Get the geographic area in sq. metres if (theFeature.Shape != null && theFeature.Shape.IsEmpty == false) { IGeometry theGeometry = theFeature.ShapeCopy; theGeometry.Project(SpatialReferenceHelper.BCAlbersSpatialReference); areaGeoValue = ((IArea)theGeometry).Area; } if (theRC == null) { // Db Area is on the feature areaDbValue = Convert.ToDouble(theFeature.get_Value(areaFieldIndex)); areaDbValue *= conversionFactor; } else { // Db Area is on related feature // Only valid if there is exactly one related record // and it has a real area (not zero or null) ISet theSet = theRC.GetObjectsRelatedToObject((IObject)theFeature); if (theSet.Count == 1) { IObject theRelatedObject = (IObject)theSet.Next(); if (theRelatedObject != null) { object theValue = theRelatedObject.get_Value(areaFieldIndex); if (Convert.IsDBNull(theValue) == false) { double theDValue = Convert.ToDouble(theValue); if (theDValue > 0) { areaDbValue = theDValue; areaDbValue *= conversionFactor; } } } } } if (double.IsNaN(areaDbValue)) { /* IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Null database area"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDS2.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); if (theRC != null) { theInfo.AddProperty("Related table", theDS1.Name); object fkValue = theFeature.get_Value(fkIndex); theInfo.AddProperty("FK value", fkValue == null ? "NULL" : fkValue.ToString()); } theError.ExtendedData = theInfo.WriteXML(); this._errors.Add(theError); */ } else if (double.IsNaN(areaGeoValue)) { IPoint theErrorPoint = this.get_ErrorPoint(null); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Null geographic area"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDS2.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); if (theRC != null) { theInfo.AddProperty("Related table", theDS1.Name); object fkValue = theFeature.get_Value(fkIndex); theInfo.AddProperty("SID", fkValue == null ? "NULL" : fkValue.ToString()); } theError.ExtendedData = theInfo.WriteXML(); this._errors.Add(theError); } else { double difference = Math.Abs(areaDbValue - areaGeoValue); if (difference > tolerance) { // Error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; if (difference < tolerance * 2) theError.Severity = 3; else if (difference < tolerance * 10) theError.Severity = 2; else theError.Severity = 1; theError.Description = "Stored area different from geographic area"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDS2.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); theInfo.AddProperty("Geographic area (sq. metres)", areaGeoValue.ToString()); theInfo.AddProperty("Database area (sq. metres)", areaDbValue.ToString()); if (theRC != null) { theInfo.AddProperty("Related table", theDS1.Name); object fkValue = theFeature.get_Value(fkIndex); theInfo.AddProperty("SID", fkValue == null ? "NULL" : fkValue.ToString()); } theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); } } theFeature = checkCursor.NextFeature(); } } catch (Exception ex) { this.LogMessage("Error comparing areas:" + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace); } }
public override int Execute(string logfileName) { this.InitLogging(logfileName); this.LogMessage(this.Name + " QA test execution started."); int currentOid = -1; try { this.LogMessage(this.Name + " Parameters:"); for (int i = 0; i < this.ParameterCount; i++) this.LogMessage(this.get_ParameterText(i) + ": " + this.get_ParameterValue(i)); this.ClearErrors(); // Check the search radius int idx = this.FindParameter("search-radius"); DoubleParameterInfo theInfo = (DoubleParameterInfo)this._params[idx]; double theSearchRadius = (double)theInfo.ParamValue; if (theSearchRadius <= 0) { this.LogMessage("Zero or negative value passed as search radius for " + this.Name + ". Stopping."); return -1; } // Check the cluster tolerance idx = this.FindParameter("cluster-tolerance"); theInfo = (DoubleParameterInfo)this._params[idx]; double theCluster = (double)theInfo.ParamValue; if (theCluster >= theSearchRadius) { this.LogMessage("Cluster tolerance equal to or larger than search radius for " + this.Name + ". Stopping."); return -1; } // Convert them to metres idx = this.FindParameter("search-units"); UnitsParameterInfo theUnitsParam = (UnitsParameterInfo)this._params[idx]; esriUnits theUnits = theUnitsParam.ParamValueInUnits; IUnitConverter theConverter = new UnitConverterClass(); theSearchRadius = theConverter.ConvertUnits(theSearchRadius, theUnits, esriUnits.esriMeters); theCluster = theConverter.ConvertUnits(theCluster, theUnits, esriUnits.esriMeters); // Get the canDefer/canExcept params idx = this.FindParameter(ParameterInfo.PARAM_CANDEFER); bool canDefer = (bool)((ParameterInfo)this._params[idx]).ParamValue; idx = this.FindParameter(ParameterInfo.PARAM_CANEXCEPT); bool canExcept = (bool)((ParameterInfo)this._params[idx]).ParamValue; // Loop through the layers for (int i = 0; i < this.LayerCount - 1; i++) { IFeatureLayer theLayer1 = this.get_Layer(i); if (this.SupportsGeometryType(theLayer1.FeatureClass.ShapeType) == false) continue; // Index the points in theLayer1 IDataset theDataset1 = (IDataset)theLayer1.FeatureClass; IGeoDataset theGeoDS = (IGeoDataset)theDataset1; IEnvelope theAOI = theGeoDS.Extent; if (theAOI == null || theAOI.IsEmpty) continue; theAOI.Project(SpatialReferenceHelper.BCAlbersSpatialReference); double theCellsize = theAOI.Width / 100; this.LogMessage("Indexing features in " + theDataset1.Name + ". Cellsize (m): " + theCellsize); PointCollectionIndex theIndex = new PointCollectionIndex(theCellsize); IFeatureCursor theFCursor1 = null; if (this.ConstrainToSelection) { ICursor theCursor = null; IFeatureSelection theFSel = (IFeatureSelection)theLayer1; theFSel.SelectionSet.Search(null, true, out theCursor); theFCursor1 = (IFeatureCursor)theCursor; } else { theFCursor1 = theLayer1.Search(null, true); } IFeature theFeature1 = theFCursor1.NextFeature(); while (theFeature1 != null) { if (theFeature1.Shape != null && theFeature1.Shape.IsEmpty == false) { IPointCollection thePtColl = (IPointCollection)theFeature1.Shape; for (int k = 0; k < thePtColl.PointCount; k++) { theIndex.AddPoint(new IndexPoint(theFeature1.OID, k, thePtColl.get_Point(k))); } } theFeature1 = theFCursor1.NextFeature(); } // Release the cursor Marshal.ReleaseComObject(theFCursor1); theFCursor1 = null; if (theIndex.PointCount > 0) { // Use the index to analyze the other layers for (int j = i+1; j < this.LayerCount; j++) { IFeatureLayer theLayer2 = this.get_Layer(j); if (this.SupportsGeometryType(theLayer2.FeatureClass.ShapeType) == false) continue; IDataset theDataset2 = (IDataset)theLayer2.FeatureClass; IFeatureCursor theFCursor2 = null; if (this.ConstrainToSelection) { ICursor theCursor = null; IFeatureSelection theFSel = (IFeatureSelection)theLayer2; theFSel.SelectionSet.Search(null, true, out theCursor); theFCursor2 = (IFeatureCursor)theCursor; } else { theFCursor2 = theLayer2.Search(null, true); } IFeature theFeature2 = theFCursor2.NextFeature(); while (theFeature2 != null) { if (theFeature2.Shape != null && theFeature2.Shape.IsEmpty == false) { IGeometry theCopy = theFeature2.ShapeCopy; theCopy.Project(SpatialReferenceHelper.BCAlbersSpatialReference); IPointCollection thePtColl = (IPointCollection)theCopy; for (int k = 0; k < thePtColl.PointCount; k++) { // Find the closest point in the index. If it's less than // the search radius and larger than the cluster tolerance, // log as an error IPoint thePoint = thePtColl.get_Point(k); IndexPoint theIdxPoint = theIndex.get_ClosestPointWithinLimits( thePoint.X, thePoint.Y, theCluster, theSearchRadius); if (theIdxPoint != null) { // Build a straight line between the offending points IPolyline thePolyline = new PolylineClass(); thePolyline.Project(SpatialReferenceHelper.BCAlbersSpatialReference); ((IPointCollection)thePolyline).AddPoint(thePoint, ref this._missing, ref this._missing); IPoint theOtherPoint = new PointClass(); theOtherPoint.Project(SpatialReferenceHelper.BCAlbersSpatialReference); theOtherPoint.PutCoords(theIdxPoint.X, theIdxPoint.Y); ((IPointCollection)thePolyline).AddPoint(theOtherPoint, ref this._missing, ref this._missing); // Get the distance double dist = PointCollectionIndex.get_Distance(thePoint.X, thePoint.Y, theOtherPoint.X, theOtherPoint.Y); // Project to geographics for error reporting thePolyline.Project(SpatialReferenceHelper.GeographicReference); // Error point will be 1/2 way along line between points IPoint theErrorPoint = new PointClass(); thePolyline.QueryPoint(esriSegmentExtension.esriNoExtension, 0.5, true, theErrorPoint); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; // If it's in the smallest 10% of the search radius, mark high severity // 50%, medium if (dist <= theSearchRadius / 10) theError.Severity = 1; else if (dist <= theSearchRadius / 2) theError.Severity = 2; else theError.Severity = 3; theError.Description = "Points almost but not quite co-located"; ExtendedInfo theExtInfo = new ExtendedInfo(); theExtInfo.AddProperty("Feature class 1", theDataset1.Name); theExtInfo.AddProperty("Feature class 2", theDataset2.Name); if (theLayer1.FeatureClass.HasOID) theExtInfo.AddProperty("Feature ID 1", theIdxPoint.SourceFeatureID.ToString()); if (theLayer2.FeatureClass.HasOID) theExtInfo.AddProperty("Feature ID 2", theFeature2.OID.ToString()); theExtInfo.AddProperty("Distance", String.Format("{0:0.## metres}", dist)); theExtInfo.AddProperty("From point x", thePolyline.FromPoint.X.ToString()); theExtInfo.AddProperty("From point y", thePolyline.FromPoint.Y.ToString()); theExtInfo.AddProperty("To point x", thePolyline.ToPoint.X.ToString()); theExtInfo.AddProperty("To point y", thePolyline.ToPoint.Y.ToString()); theError.ExtendedData = theExtInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); } } } theFeature2 = theFCursor2.NextFeature(); } } } } this.LogMessage("Number of errors found: " + this.ErrorCount); this.LogMessage("Test " + this.Name + " successful."); } catch (Exception ex) { this.LogMessage("Exception caught: \n" + ex.Message + "\n" + ex.StackTrace.ToString()); this.LogMessage("id of the current polygon: " + currentOid); return -1; } finally { this.StopLogging(); } return this.ErrorCount; }
private void CheckSIDs(IFeatureClass workingFeatureClass, IFeatureCursor checkCursor, string sidFieldName, bool canDefer, bool canExcept) { IDataset theDS2 = (IDataset)workingFeatureClass; Hashtable theLookup = new Hashtable(); try { int sidIndex = workingFeatureClass.FindField(sidFieldName); IFeature theFeature = checkCursor.NextFeature(); while (theFeature != null) { object theSID = theFeature.get_Value(sidIndex); if (theSID == null || Convert.IsDBNull(theSID)) { // Error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Null SID"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDS2.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); } else if (theLookup.ContainsKey(theSID)) { // Error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Non-unique SID"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDS2.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); object sidValue = theFeature.get_Value(sidIndex); theInfo.AddProperty("SID value", sidValue.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); this._errors.Add(theError); } else { theLookup.Add(theSID, theSID); } theFeature = checkCursor.NextFeature(); } } catch (Exception ex) { this.LogMessage("Error checking SID uniqueness and completeness:" + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace); } }
private ArrayList ProcessFeature(IFeature test, SegmentCollectionIndex index, double searchRadius, double aspectRatio, bool canDefer, bool canExcept) { if (test == null || test.Shape == null || test.Shape.IsEmpty) return null; ArrayList theReturn = new ArrayList(); IDataset theDataset = (IDataset)test.Table; IGeometry theTestShape = test.ShapeCopy; theTestShape.Project(SpatialReferenceHelper.BCAlbersSpatialReference); ISegmentCollection theSegColl = (ISegmentCollection)theTestShape; ISegment theSegment; for (int i = 0; i < theSegColl.SegmentCount; i++) { theSegment = theSegColl.get_Segment(i); int theNeatlineOID; double dist1 = index.get_MinimumDistance(theSegment.FromPoint, out theNeatlineOID); if (dist1 <= searchRadius) { double dist2 = index.get_MinimumDistance(theSegment.ToPoint, out theNeatlineOID); if (dist2 <= searchRadius) { if (Math.Abs(dist1 - dist2) < theSegment.Length / aspectRatio) { // Error point will be 1/2 way along segment IPolyline thePolyline = new PolylineClass(); thePolyline.Project(SpatialReferenceHelper.BCAlbersSpatialReference); ((ISegmentCollection)thePolyline).AddSegment(theSegment, ref this._missing, ref this._missing); thePolyline.Project(SpatialReferenceHelper.GeographicReference); IPoint theErrorPoint = new PointClass(); thePolyline.QueryPoint(esriSegmentExtension.esriNoExtension, 0.5, true, theErrorPoint); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; // If it's in the smallest 10% of the search radius, mark high severity // 50%, medium double avgDist = (dist1 + dist2) / 2; if (avgDist <= searchRadius / 10) theError.Severity = 1; else if (avgDist <= searchRadius / 2) theError.Severity = 2; else theError.Severity = 3; theError.Description = "Line segment co-linear with neatline"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDataset.Name); if (test.HasOID) theInfo.AddProperty("Feature ID", test.OID.ToString()); theInfo.AddProperty("Neatline Feature ID", theNeatlineOID.ToString()); theInfo.AddProperty("Distance", String.Format("{0:0.## metres}", avgDist)); theInfo.AddProperty("From point x", thePolyline.FromPoint.X.ToString()); theInfo.AddProperty("From point y", thePolyline.FromPoint.Y.ToString()); theInfo.AddProperty("To point x", thePolyline.ToPoint.X.ToString()); theInfo.AddProperty("To point y", thePolyline.ToPoint.Y.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); theReturn.Add(theError); } } } } return theReturn; }
private ArrayList WeedErrors(ArrayList potentialList, bool canDefer, bool canExcept, double minGap, double maxGap) { /* * Finds the smallest gap between a given source feature vertex and another feature * This cuts down on the noise considerably. Without this, a source vertex could show * gap errors to multiple vertices on another feature if those vertices were closer * than the maxGap */ ArrayList theReturn = new ArrayList(); // Organize the errors by key // Key is defined as source oid + source vertex # + other oid Hashtable theHash = new Hashtable(); foreach (object obj in potentialList) { PotentialError thePError = (PotentialError)obj; string key = thePError.Key; ArrayList theErrorsWithSameKey; if (theHash.ContainsKey(key) == false) { theErrorsWithSameKey = new ArrayList(2); theHash.Add(key, theErrorsWithSameKey); } else { theErrorsWithSameKey = (ArrayList)theHash[key]; } theErrorsWithSameKey.Add(thePError); } // Get the PotentialError with the smallest GapSize foreach (object key in theHash.Keys) { ArrayList theErrorsWithSameKey = (ArrayList)theHash[key]; PotentialError theSmallest = null; foreach (object obj in theErrorsWithSameKey) { PotentialError thePError = (PotentialError)obj; if (theSmallest == null || thePError.GapSize < theSmallest.GapSize) theSmallest = thePError; } if (theSmallest != null) { DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theSmallest.ErrorPoint; // If it's in the smallest 10% of the gap range, mark high severity if (theSmallest.GapSize < (minGap + ((maxGap - minGap) / 10))) theError.Severity = 1; else theError.Severity = 2; theError.Description = "Gap between polygons"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theSmallest.FeatureClassName); theInfo.AddProperty("Gap size", String.Format("{0:0.## metres}", theSmallest.GapSize)); theInfo.AddProperty("Angle", String.Format("{0:0.## degrees}", theSmallest.AngleDegrees)); theInfo.AddProperty("From point x", theSmallest.FromPoint.X.ToString()); theInfo.AddProperty("From point y", theSmallest.FromPoint.Y.ToString()); theInfo.AddProperty("To point x", theSmallest.ToPoint.X.ToString()); theInfo.AddProperty("To point y", theSmallest.ToPoint.Y.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); theReturn.Add(theError); } } return theReturn; }
private ArrayList ProcessFeature(IFeature test) { if (test == null || test.Shape == null || test.Shape.IsEmpty) return null; ArrayList theReturn = new ArrayList(); int index = this.FindParameter("minimum-allowable-ap-ratio"); double apRatio = Math.Abs((double)((ParameterInfo)this._params[index]).ParamValue); index = this.FindParameter(ParameterInfo.PARAM_CANDEFER); bool canDefer = (bool)((ParameterInfo)this._params[index]).ParamValue; index = this.FindParameter(ParameterInfo.PARAM_CANEXCEPT); bool canExcept = (bool)((ParameterInfo)this._params[index]).ParamValue; IGeometry theGeometry = test.Shape; bool bNeedProjection = !SpatialReferenceHelper.IsBCAlbers(theGeometry.SpatialReference); if (bNeedProjection) theGeometry.Project(SpatialReferenceHelper.BCAlbersSpatialReference); IGeometryCollection theGeomColl = (IGeometryCollection)test.Shape; for (int i = 0; i < theGeomColl.GeometryCount; i++) { IGeometry theGeometryPart = theGeomColl.get_Geometry(i); IArea theArea = (IArea)theGeometryPart; if (theArea.Area > 0) { ICurve theCurve = (ICurve)theGeometryPart; double calculatedAP = theArea.Area / theCurve.Length; if (calculatedAP < apRatio) { IPoint theErrorPoint = this.get_ErrorPoint(theGeometryPart); theErrorPoint.Project(SpatialReferenceHelper.GeographicReference); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; if (calculatedAP < apRatio / 10) theError.Severity = 1; else if (calculatedAP < apRatio / 2) theError.Severity = 2; else theError.Severity = 3; theError.Description = "Sliver polygon"; ExtendedInfo theInfo = new ExtendedInfo(); IDataset theDataset = (IDataset)test.Table; theInfo.AddProperty("Feature class", theDataset.Name); theInfo.AddProperty("Feature ID", test.OID.ToString()); theInfo.AddProperty("Part area", String.Format("{0:0.##}", theArea.Area) + " sq. metres"); theInfo.AddProperty("Area/Perimeter ratio", String.Format("{0:0.##}", calculatedAP)); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); theReturn.Add(theError); } } } return theReturn; }
private ArrayList ProcessFeature(ITopologyErrorFeature test, IDataset source, string subtypeName, double maxOverlap, double minOverlap) { if (test == null) return null; IFeature theTestFeature = (IFeature)test; if (theTestFeature.Shape == null || theTestFeature.Shape.IsEmpty) return null; if (test.ShapeType != esriGeometryType.esriGeometryPolygon) return null; ArrayList theReturn = new ArrayList(); int index = this.FindParameter(ParameterInfo.PARAM_CANDEFER); bool canDefer = (bool)((ParameterInfo)this._params[index]).ParamValue; index = this.FindParameter(ParameterInfo.PARAM_CANEXCEPT); bool canExcept = (bool)((ParameterInfo)this._params[index]).ParamValue; IGeometry theGeometry = theTestFeature.ShapeCopy; theGeometry.Project(SpatialReferenceHelper.BCAlbersSpatialReference); // Is/isn't an error based on the full area of the error IArea theArea = (IArea)theGeometry; bool isError = (theArea.Area > minOverlap && theArea.Area < maxOverlap); if (isError) { // Severity is based on parts IGeometryCollection theGeomColl = (IGeometryCollection)theGeometry; for (int i = 0; i < theGeomColl.GeometryCount; i++) { IGeometry theGeometryPart = theGeomColl.get_Geometry(i); IArea thePartArea = (IArea)theGeometryPart; if (thePartArea.Area > 0) { IPoint theErrorPoint = this.get_ErrorPoint(theGeometryPart); theErrorPoint.Project(SpatialReferenceHelper.GeographicReference); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; if (thePartArea.Area > minOverlap && thePartArea.Area < maxOverlap) theError.Severity = 1; else theError.Severity = 2; theError.Description = "Polygons overlap"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", source.Name); theInfo.AddProperty("Feature subtype", subtypeName); theInfo.AddProperty("Feature ID #1", test.OriginOID.ToString()); theInfo.AddProperty("Feature ID #2", test.DestinationOID.ToString()); theInfo.AddProperty("Area", thePartArea.Area.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); theReturn.Add(theError); } } } return theReturn; }
private ArrayList CheckFeatureTypes( IFeatureClass workingFeatureClass, string unqualifiedFClassName, IFeatureCursor checkCursor, string ftFieldName, Hashtable featureTypeData, bool canDefer, bool canExcept) { ArrayList theReturn = new ArrayList(); IDataset theDS2 = (IDataset)workingFeatureClass; int ftIndex = workingFeatureClass.FindField(ftFieldName); int stIndex = -1; if (workingFeatureClass is ISubtypes && ((ISubtypes)workingFeatureClass).HasSubtype) { ISubtypes theST = (ISubtypes)workingFeatureClass; stIndex = theST.SubtypeFieldIndex; } try { IFeature theFeature = checkCursor.NextFeature(); while (theFeature != null) { object theFCode = theFeature.get_Value(ftIndex); if (theFCode == null || Convert.IsDBNull(theFCode)) { // Null FCode Error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Null Feature Code"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDS2.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); theReturn.Add(theError); } else { // Determine the featureclass/subtype theFCode = theFCode.ToString(); string theKey = unqualifiedFClassName; if (stIndex >= 0) theKey += ":" + theFeature.get_Value(stIndex); // Get the allowed set of feature codes Hashtable theAllowedCodes = null; if (featureTypeData.ContainsKey(theKey)) theAllowedCodes = (Hashtable)featureTypeData[theKey]; if (theAllowedCodes == null) { // Unknown subtype error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Unknown Subtype Code"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDS2.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); theInfo.AddProperty("Subtype", theFeature.get_Value(stIndex).ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); theReturn.Add(theError); } else if (theAllowedCodes.ContainsKey(theFCode) == false) { // Invalid FCode Error IPoint theErrorPoint = this.get_ErrorPoint(theFeature.Shape); DataQualityError theError = new DataQualityError(this.Name, canDefer, canExcept); theError.Location = theErrorPoint; theError.Severity = 1; theError.Description = "Invalid Feature Type"; ExtendedInfo theInfo = new ExtendedInfo(); theInfo.AddProperty("Feature class", theDS2.Name); theInfo.AddProperty("Feature ID", theFeature.OID.ToString()); if (stIndex >= 0) theInfo.AddProperty("Subtype", theFeature.get_Value(stIndex).ToString()); string theCodes = ""; string theDescs = ""; foreach (object key in theAllowedCodes.Keys) { if (theCodes.Length > 0) { theCodes += ", "; theDescs += ", "; } theCodes += key; theDescs += theAllowedCodes[key]; } theInfo.AddProperty("Allowed feature codes", theCodes); theInfo.AddProperty("Allowed feature types", theDescs); theInfo.AddProperty("Feature type", theFCode.ToString()); theError.ExtendedData = theInfo.WriteXML(); //this.LogMessage(theError.ExtendedData); theReturn.Add(theError); } } theFeature = checkCursor.NextFeature(); } } catch (Exception ex) { this.LogMessage("Error checking feature codes:" + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace); } return theReturn; }