About the Import signposts Sample
[C#]
SignpostUtilities.cs
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); } } } }
[Visual Basic .NET]
SignpostUtilities.vb
Imports System Imports System.Runtime.InteropServices Imports System.Collections Imports ESRI.ArcGIS.esriSystem Imports ESRI.ArcGIS.Geodatabase Imports ESRI.ArcGIS.Geoprocessing Imports ESRI.ArcGIS.Geometry Namespace GPImportSignpostFunctions Public Class SignpostUtilities Public Shared ReadOnly MaxBranchCount As Integer = 5 Public Structure FeatureData Public Sub New(ByVal ID As Integer, ByVal geom As IGeometry) OID = ID feature = geom End Sub Public OID As Integer Public feature As IGeometry End Structure Public Sub New() End Sub Public Shared Function CreateSignsFeatureClass(ByVal linesFeatureClass As IFeatureClass, ByVal name As String) As IFeatureClass ' 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 Dim fcDescription As IFeatureClassDescription = New FeatureClassDescription Dim ocDescription As IObjectClassDescription = CType(fcDescription, IObjectClassDescription) Dim outFields As IFieldsEdit = CType(ocDescription.RequiredFields, IFieldsEdit) ' make the shape field to be of type polyline with the same spatial reference as the reference lines Dim shapeField As IField = outFields.Field(outFields.FindField(fcDescription.ShapeFieldName)) Dim geomDefEdit As IGeometryDefEdit = CType(shapeField.GeometryDef, IGeometryDefEdit) geomDefEdit.GeometryType_2 = esriGeometryType.esriGeometryPolyline geomDefEdit.SpatialReference_2 = CType(linesFeatureClass, IGeoDataset).SpatialReference ' add the other fields to the feature class Dim field As IFieldEdit = New Field field.Name_2 = "ExitName" field.Type_2 = esriFieldType.esriFieldTypeString field.Length_2 = 10 outFields.AddField(field) Dim currentNumber As String For i As Integer = 0 To MaxBranchCount - 1 currentNumber = Convert.ToString(i) field = New Field field.Name_2 = "Branch" + currentNumber field.Type_2 = esriFieldType.esriFieldTypeString field.Length_2 = 75 outFields.AddField(field) field = New Field field.Name_2 = "Branch" + currentNumber + "Dir" field.Type_2 = esriFieldType.esriFieldTypeString field.Length_2 = 5 outFields.AddField(field) field = New Field field.Name_2 = "Branch" + currentNumber + "Lng" field.Type_2 = esriFieldType.esriFieldTypeString field.Length_2 = 2 outFields.AddField(field) field = New Field field.Name_2 = "Toward" + currentNumber field.Type_2 = esriFieldType.esriFieldTypeString field.Length_2 = 75 outFields.AddField(field) field = New Field field.Name_2 = "Toward" + currentNumber + "Lng" field.Type_2 = esriFieldType.esriFieldTypeString field.Length_2 = 2 outFields.AddField(field) Next i ' make the feature class Dim featureDataset As IFeatureDataset = linesFeatureClass.FeatureDataset Dim workspace As IWorkspace = CType(linesFeatureClass, IDataset).Workspace If Not featureDataset Is Nothing Then Return featureDataset.CreateFeatureClass(name, outFields, ocDescription.InstanceCLSID, ocDescription.ClassExtensionCLSID, _ esriFeatureType.esriFTSimple, fcDescription.ShapeFieldName, "") ElseIf TypeOf workspace Is IFeatureWorkspace Then Return CType(workspace, IFeatureWorkspace).CreateFeatureClass(name, outFields, ocDescription.InstanceCLSID, ocDescription.ClassExtensionCLSID, _ esriFeatureType.esriFTSimple, fcDescription.ShapeFieldName, "") Else Return Nothing ' not expected End If End Function Public Shared Function CreateSignsDetailTable(ByVal linesFeatureClass As IFeatureClass, ByVal name As String) As ITable ' 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 Dim ocDescription As IObjectClassDescription = New ObjectClassDescription Dim outFields As IFieldsEdit = CType(ocDescription.RequiredFields, IFieldsEdit) ' add the SignpostID field to the table Dim field As IFieldEdit = New Field field.Name_2 = "SignpostID" field.Type_2 = esriFieldType.esriFieldTypeInteger outFields.AddField(field) ' add the other fields to the table field = New Field field.Name_2 = "Sequence" field.Type_2 = esriFieldType.esriFieldTypeInteger outFields.AddField(field) field = New Field field.Name_2 = "EdgeFCID" field.Type_2 = esriFieldType.esriFieldTypeInteger outFields.AddField(field) field = New Field field.Name_2 = "EdgeFID" field.Type_2 = esriFieldType.esriFieldTypeInteger outFields.AddField(field) field = New Field field.Name_2 = "EdgeFrmPos" field.Type_2 = esriFieldType.esriFieldTypeDouble outFields.AddField(field) field = New Field field.Name_2 = "EdgeToPos" field.Type_2 = esriFieldType.esriFieldTypeDouble outFields.AddField(field) ' make the table Dim featureDataset As IFeatureDataset = linesFeatureClass.FeatureDataset Dim workspace As IWorkspace = CType(linesFeatureClass, IDataset).Workspace If Not featureDataset Is Nothing Then ' up a level Dim createWS As IFeatureWorkspace = CType(featureDataset.Workspace, IFeatureWorkspace) Return createWS.CreateTable(name, outFields, ocDescription.InstanceCLSID, ocDescription.ClassExtensionCLSID, "") ElseIf TypeOf workspace Is IFeatureWorkspace Then Return CType(workspace, IFeatureWorkspace).CreateTable(name, outFields, ocDescription.InstanceCLSID, ocDescription.ClassExtensionCLSID, "") Else Return Nothing ' not expected End If End Function Public Shared Function FillFeatureCache(ByVal inputSignsTable As ITable, ByVal inFromIDFI As Integer, ByVal inToIDFI As Integer, ByVal inputLineFeatures As IFeatureClass, ByVal linesIDFieldName As String, ByVal trackcancel As ITrackCancel) As Hashtable ' 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. Dim IDs As SortedList = New System.Collections.SortedList() Dim inCursor As ICursor = inputSignsTable.Search(Nothing, True) Dim row As IRow Dim fromID As Long, toID As Long Dim exists As Boolean Dim cancelCheckInterval As Integer = 100 row = inCursor.NextRow() If inToIDFI = -1 Then While Not row Is Nothing fromID = Convert.ToInt64(row.Value(inFromIDFI)) exists = IDs.Contains(fromID) If Not exists Then IDs.Add(fromID, fromID) End If row = inCursor.NextRow() End While Else While Not row Is Nothing fromID = Convert.ToInt64(row.Value(inFromIDFI)) toID = Convert.ToInt64(row.Value(inToIDFI)) exists = IDs.Contains(fromID) If Not exists Then IDs.Add(fromID, fromID) End If exists = IDs.Contains(toID) If Not exists Then IDs.Add(toID, toID) End If row = inCursor.NextRow() End While End If ' make the query filter for fetching features Dim queryFilter As IQueryFilter = New QueryFilter queryFilter.SubFields = "*" ' Now fetch batches of features Dim currID As Long Dim numFeaturesPerQuery As Integer = 200 Dim numToFetch As Integer = IDs.Count Dim totalRemaining As Integer, totalDone As Integer = 0 Dim linesIDFieldIndex As Integer = inputLineFeatures.FindField(linesIDFieldName) Dim outputFeatures As Hashtable = New System.Collections.Hashtable(CType(numToFetch, Integer)) If numFeaturesPerQuery > numToFetch Then numFeaturesPerQuery = numToFetch End If 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). Dim whereClause As String = linesIDFieldName + " IN(" For i As Integer = 0 To numFeaturesPerQuery - 1 currID = Convert.ToInt64(IDs.GetByIndex(totalDone + i)) whereClause += Convert.ToString(currID) If i <> (numFeaturesPerQuery - 1) Then whereClause += "," Else whereClause += ")" End If Next i queryFilter.WhereClause = whereClause ' select the features Dim inputFeatureCursor As IFeatureCursor = inputLineFeatures.Search(queryFilter, False) ' get the features Dim feature As IFeature feature = inputFeatureCursor.NextFeature() While Not feature Is Nothing ' 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 Dim data As FeatureData = New FeatureData(feature.OID, feature.ShapeCopy) outputFeatures.Add(Convert.ToInt64(feature.Value(linesIDFieldIndex)), data) Catch End Try If (totalDone Mod cancelCheckInterval) = 0 Then ' check for user cancel If Not trackcancel.Continue() Then Throw (New COMException("Function cancelled.")) End If End If feature = inputFeatureCursor.NextFeature() End While ' finished? set up for next batch totalDone += numFeaturesPerQuery totalRemaining = numToFetch - totalDone If totalRemaining > 0 Then If numFeaturesPerQuery > totalRemaining Then numFeaturesPerQuery = totalRemaining End If End If End While Return outputFeatures End Function Public Shared Sub CleanUpSignpostFeatureValues(ByVal featureBuffer As IFeatureBuffer, ByVal lastValidBranchNum As Integer, ByVal lastValidTowardNum As Integer, _ ByVal outBranchXFI As Integer(), ByVal outBranchXDirFI As Integer(), ByVal outBranchXLngFI As Integer(), _ ByVal outTowardXFI As Integer(), ByVal outTowardXLngFI As Integer()) ' set unused sequence number values to null (our row buffer may still ' have junk at the end) For i As Integer = lastValidBranchNum + 1 To SignpostUtilities.MaxBranchCount - 1 featureBuffer.Value(outBranchXFI(i)) = Nothing featureBuffer.Value(outBranchXDirFI(i)) = Nothing featureBuffer.Value(outBranchXLngFI(i)) = Nothing Next i For i As Integer = lastValidTowardNum + 1 To SignpostUtilities.MaxBranchCount - 1 featureBuffer.Value(outTowardXFI(i)) = Nothing featureBuffer.Value(outTowardXLngFI(i)) = Nothing Next i End Sub End Class End Namespace