SignpostUtilities.cs
// Copyright 2010 ESRI // // All rights reserved under the copyright laws of the United States // and applicable international laws, treaties, and conventions. // // You may freely redistribute and use this sample code, with or // without modification, provided you include the original copyright // notice and use restrictions. // // See the use restrictions. // using System; using System.Runtime.InteropServices; using System.Collections; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geoprocessing; using ESRI.ArcGIS.Geometry; namespace GPImportSignpostFunctions { /// <summary> /// SignpostUtilities class has functions for creating and populating the signpost /// schema. /// </summary> public class SignpostUtilities { public static readonly int MaxBranchCount = 5; public struct FeatureData { public FeatureData(int ID, IGeometry geom) { OID = ID; feature = geom; } public int OID; public IGeometry feature; } public SignpostUtilities() { } public static IFeatureClass CreateSignsFeatureClass(IFeatureClass linesFeatureClass, string name) { // Locations are all relative to the location of the reference lines. // For geodatabase, signs feature class is at the same location and the streets table // is at the level of the containing feature dataset. // For shapefile, both are at the same location as the reference lines. // start with the initial set of required fields for a feature class IFeatureClassDescription fcDescription = new FeatureClassDescriptionClass(); IObjectClassDescription ocDescription = fcDescription as IObjectClassDescription; IFieldsEdit outFields = ocDescription.RequiredFields as IFieldsEdit; // make the shape field to be of type polyline with the same spatial reference as the reference lines IField shapeField = outFields.get_Field(outFields.FindField(fcDescription.ShapeFieldName)); IGeometryDefEdit geomDefEdit = shapeField.GeometryDef as IGeometryDefEdit; geomDefEdit.GeometryType_2 = esriGeometryType.esriGeometryPolyline; geomDefEdit.SpatialReference_2 = (linesFeatureClass as IGeoDataset).SpatialReference; // add the other fields to the feature class IFieldEdit field = new FieldClass(); field.Name_2 = "ExitName"; field.Type_2 = esriFieldType.esriFieldTypeString; field.Length_2 = 10; outFields.AddField(field); string currentNumber; for (int i = 0; i < MaxBranchCount; i++) { currentNumber = Convert.ToString(i); field = new FieldClass(); field.Name_2 = "Branch" + currentNumber; field.Type_2 = esriFieldType.esriFieldTypeString; field.Length_2 = 75; outFields.AddField(field); field = new FieldClass(); field.Name_2 = "Branch" + currentNumber + "Dir"; field.Type_2 = esriFieldType.esriFieldTypeString; field.Length_2 = 5; outFields.AddField(field); field = new FieldClass(); field.Name_2 = "Branch" + currentNumber + "Lng"; field.Type_2 = esriFieldType.esriFieldTypeString; field.Length_2 = 2; outFields.AddField(field); field = new FieldClass(); field.Name_2 = "Toward" + currentNumber; field.Type_2 = esriFieldType.esriFieldTypeString; field.Length_2 = 75; outFields.AddField(field); field = new FieldClass(); field.Name_2 = "Toward" + currentNumber + "Lng"; field.Type_2 = esriFieldType.esriFieldTypeString; field.Length_2 = 2; outFields.AddField(field); } // make the feature class IFeatureDataset pFeatureDataset = linesFeatureClass.FeatureDataset; IWorkspace pWorkspace = (linesFeatureClass as IDataset).Workspace; if (pFeatureDataset != null) return pFeatureDataset.CreateFeatureClass(name, outFields, ocDescription.InstanceCLSID, ocDescription.ClassExtensionCLSID, esriFeatureType.esriFTSimple, fcDescription.ShapeFieldName, ""); else if (pWorkspace is IFeatureWorkspace) return (pWorkspace as IFeatureWorkspace).CreateFeatureClass(name, outFields, ocDescription.InstanceCLSID, ocDescription.ClassExtensionCLSID, esriFeatureType.esriFTSimple, fcDescription.ShapeFieldName, ""); else return null; // not expected } public static ITable CreateSignsDetailTable(IFeatureClass linesFeatureClass, string name) { // Locations are all relative to the location of the reference lines. // For Geodatabase, signs feature class is at the same location and the streets table // is at the level of the containing feature dataset. // For shapefile, both are at the same location as the reference lines. // start with the initial set of required fields for a table IObjectClassDescription ocDescription = new ObjectClassDescriptionClass(); IFieldsEdit outFields = ocDescription.RequiredFields as IFieldsEdit; // add the SignpostID field to the table IFieldEdit field = new FieldClass(); field.Name_2 = "SignpostID"; field.Type_2 = esriFieldType.esriFieldTypeInteger; outFields.AddField(field); // add the other fields to the table field = new FieldClass(); field.Name_2 = "Sequence"; field.Type_2 = esriFieldType.esriFieldTypeInteger; outFields.AddField(field); field = new FieldClass(); field.Name_2 = "EdgeFCID"; field.Type_2 = esriFieldType.esriFieldTypeInteger; outFields.AddField(field); field = new FieldClass(); field.Name_2 = "EdgeFID"; field.Type_2 = esriFieldType.esriFieldTypeInteger; outFields.AddField(field); field = new FieldClass(); field.Name_2 = "EdgeFrmPos"; field.Type_2 = esriFieldType.esriFieldTypeDouble; outFields.AddField(field); field = new FieldClass(); field.Name_2 = "EdgeToPos"; field.Type_2 = esriFieldType.esriFieldTypeDouble; outFields.AddField(field); // make the table IFeatureDataset featureDataset = linesFeatureClass.FeatureDataset; IWorkspace workspace = (linesFeatureClass as IDataset).Workspace; if (featureDataset != null) { // up a level IFeatureWorkspace createWS = featureDataset.Workspace as IFeatureWorkspace; return createWS.CreateTable(name, outFields, ocDescription.InstanceCLSID, null, ""); } else if (workspace is IFeatureWorkspace) return (workspace as IFeatureWorkspace).CreateTable(name, outFields, ocDescription.InstanceCLSID, null, ""); else return null; // not expected } public static Hashtable FillFeatureCache(ITable inputSignsTable, int inFromIDFI, int inToIDFI, IFeatureClass inputLineFeatures, string linesIDFieldName, ITrackCancel trackcancel) { // make and fill a SortedList from the IDs referenced in the table // for MultiNet data, there is only one ID field, so its index will be // passed in as inFromIDFI, while -1 will be passed in to inToIDFI. SortedList IDs = new System.Collections.SortedList(); ICursor inCursor = inputSignsTable.Search(null, true); IRow row; long fromID, toID; bool exists; int cancelCheckInterval = 100; if (inToIDFI == -1) { while ((row = inCursor.NextRow()) != null) { fromID = Convert.ToInt64(row.get_Value(inFromIDFI)); exists = IDs.Contains(fromID); if (!exists) IDs.Add(fromID, fromID); } } else { while ((row = inCursor.NextRow()) != null) { fromID = Convert.ToInt64(row.get_Value(inFromIDFI)); toID = Convert.ToInt64(row.get_Value(inToIDFI)); exists = IDs.Contains(fromID); if (!exists) IDs.Add(fromID, fromID); exists = IDs.Contains(toID); if (!exists) IDs.Add(toID, toID); } } // make the query filter for fetching features IQueryFilter queryFilter = new QueryFilterClass(); queryFilter.SubFields = "*"; // Now fetch batches of features long currID; int numFeaturesPerQuery = 200; int numToFetch = IDs.Count; int totalRemaining, totalDone = 0; int linesIDFieldIndex = inputLineFeatures.FindField(linesIDFieldName); Hashtable outputFeatures = new System.Collections.Hashtable((int)numToFetch); if (numFeaturesPerQuery > numToFetch) numFeaturesPerQuery = numToFetch; while (totalDone < numToFetch) { // Populate the QueryDef Where clause IN() statement for the current batch of features. // This is going to be very slow unless linesIDFieldName is indexed and this is why // we added a warning to the GP message window if this is the case. If you cannot // index linesIDFieldName, then this code would run faster scanning the whole feature // class looking for the records we need (no Where clause). string whereClause = linesIDFieldName + " IN("; for (int i = 0; i < numFeaturesPerQuery; i++) { currID = Convert.ToInt64(IDs.GetByIndex(totalDone + i)); whereClause += Convert.ToString(currID); if (i != (numFeaturesPerQuery - 1)) whereClause += ","; else whereClause += ")"; } queryFilter.WhereClause = whereClause; // select the features IFeatureCursor inputFeatureCursor = inputLineFeatures.Search(queryFilter, false); // get the features IFeature feature; while ((feature = inputFeatureCursor.NextFeature()) != null) { // keep a copy of the OID and shape of feature - skip records that cause errors // (perhaps pass the GPMessages in and log warnings in there if you need to log exceptions) try { FeatureData data = new FeatureData(feature.OID, feature.ShapeCopy); outputFeatures.Add(Convert.ToInt64(feature.get_Value(linesIDFieldIndex)), data); } catch { } if ((totalDone % cancelCheckInterval) == 0) { // check for user cancel if (!trackcancel.Continue()) throw (new COMException("Function cancelled.")); } } // finished? set up for next batch totalDone += numFeaturesPerQuery; totalRemaining = numToFetch - totalDone; if (totalRemaining > 0) { if (numFeaturesPerQuery > totalRemaining) numFeaturesPerQuery = totalRemaining; } } return outputFeatures; } public static void CleanUpSignpostFeatureValues(IFeatureBuffer featureBuffer, int lastValidBranchNum, int lastValidTowardNum, int[] outBranchXFI, int[] outBranchXDirFI, int[] outBranchXLngFI, int[] outTowardXFI, int[] outTowardXLngFI) { // set unused sequence number values to null (our row buffer may still // have junk at the end) for (int i = lastValidBranchNum + 1; i < SignpostUtilities.MaxBranchCount; i++) { featureBuffer.set_Value(outBranchXFI[i], null); featureBuffer.set_Value(outBranchXDirFI[i], null); featureBuffer.set_Value(outBranchXLngFI[i], null); } for (int i = lastValidTowardNum + 1; i < SignpostUtilities.MaxBranchCount; i++) { featureBuffer.set_Value(outTowardXFI[i], null); featureBuffer.set_Value(outTowardXLngFI[i], null); } } } }