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