About the Import signposts Sample
[C#]
ImportNavStreetsSignsFunction.cs
using System;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geoprocessing;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Carto;
namespace GPImportSignpostFunctions
{
/// <summary>
/// Summary description for ImportNavStreetsSignsFunction.
/// </summary>
///
[Guid("DCC9BF27-9DAC-4d00-A172-690467B3D1E4")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("GPImportSignpostFunctions.ImportNavStreetsSignsFunction")]
public class ImportNavStreetsSignsFunction : IGPFunction
{
#region Constants
// parameter index constants
private const int InputTable = 0;
private const int ReferenceLineFeatures = 1;
private const int OutFeatureClassName = 2;
private const int OutStreetsTableName = 3;
// field names and types
private static readonly string[] FieldNames = new string[]
{ "SRC_LINKID", "DST_LINKID", "SIGN_ID", "SEQ_NUM", "EXIT_NUM",
"BR_RTEID", "BR_RTEDIR", "SIGN_TXTTP", "SIGN_TEXT", "LANG_CODE",
"TOW_RTEID", "SIGN_TXTTP" };
private static readonly esriFieldType[] FieldTypes = new esriFieldType[]
{ esriFieldType.esriFieldTypeDouble,
esriFieldType.esriFieldTypeDouble,
esriFieldType.esriFieldTypeDouble,
esriFieldType.esriFieldTypeSmallInteger,
esriFieldType.esriFieldTypeString,
esriFieldType.esriFieldTypeString,
esriFieldType.esriFieldTypeString,
esriFieldType.esriFieldTypeString,
esriFieldType.esriFieldTypeString,
esriFieldType.esriFieldTypeString,
esriFieldType.esriFieldTypeString,
esriFieldType.esriFieldTypeString};
private static readonly string LinesIDFieldName = "LINK_ID";
private static readonly esriFieldType LinesIDFieldType = esriFieldType.esriFieldTypeDouble;
#endregion
private IArray m_parameters;
public ImportNavStreetsSignsFunction()
{
}
#region IGPFunction Members
public IArray ParameterInfo
{
// create and return the parameters for this function:
// 1 - Input Signs Table
// 2 - Input Street Features
// 3 - Output Signpost Feature Class Name
// 4 - Output Signpost Streets Table Name
get
{
IArray paramArray = new ArrayClass();
// 1 - input_signs_table
IGPParameterEdit paramEdit = new GPParameterClass();
paramEdit.DataType = new DETableTypeClass() as IGPDataType;
paramEdit.Value = new DETableClass() as IGPValue;
paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput;
paramEdit.DisplayName = "Input Signs Table";
paramEdit.Enabled = true;
paramEdit.Name = "input_signs_table";
paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;
paramArray.Add(paramEdit as object);
// 2 - reference_street_features
paramEdit = new GPParameterClass();
paramEdit.DataType = new DEFeatureClassTypeClass() as IGPDataType;
paramEdit.Value = new DEFeatureClass() as IGPValue;
paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput;
paramEdit.DisplayName = "Input Street Features";
paramEdit.Enabled = true;
paramEdit.Name = "reference_street_features";
paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;
IGPFeatureClassDomain lineFeatureClassDomain = new GPFeatureClassDomainClass();
lineFeatureClassDomain.AddType(esriGeometryType.esriGeometryLine);
lineFeatureClassDomain.AddType(esriGeometryType.esriGeometryPolyline);
paramEdit.Domain = lineFeatureClassDomain as IGPDomain;
paramArray.Add(paramEdit as object);
// 3 - out_feature_class_name
paramEdit = new GPParameterClass();
paramEdit.DataType = new GPStringTypeClass() as IGPDataType;
IGPString stringVal = new GPStringClass();
stringVal.Value = "Signpost";
paramEdit.Value = stringVal as IGPValue;
paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput;
paramEdit.DisplayName = "Output Signpost Feature Class Name";
paramEdit.Enabled = true;
paramEdit.Name = "out_feature_class_name";
paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;
paramArray.Add(paramEdit as object);
// 4 - out_table_name
paramEdit = new GPParameterClass();
paramEdit.DataType = new GPStringTypeClass() as IGPDataType;
stringVal = new GPStringClass();
stringVal.Value = "SignpostSt";
paramEdit.Value = stringVal as IGPValue;
paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput;
paramEdit.DisplayName = "Output Signpost Streets Table Name";
paramEdit.Enabled = true;
paramEdit.Name = "out_streets_table_name";
paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;
paramArray.Add(paramEdit as object);
// TODO: add two derived output parameters for chaining this function
// in models
return paramArray;
}
}
public IGPMessages Validate(IArray paramvalues, bool updateValues, IGPEnvironmentManager envMgr)
{
// Create the GPUtilities Object
IGPUtilities gpUtils = new GPUtilitiesClass();
// Initialize a copy of our parameters
if (m_parameters == null) m_parameters = ParameterInfo;
// Call InternalValidate to check for required parameters
IGPMessages validateMessages = gpUtils.InternalValidate(m_parameters, paramvalues, updateValues, true, envMgr);
// Verify chosen input table has the expected fields
IGPParameter gpParam = paramvalues.get_Element(InputTable) as IGPParameter;
IGPValue tableValue = gpUtils.UnpackGPValue(gpParam);
// CheckForTableFields will report errors by modifying the relevant GPMessage
if (!tableValue.IsEmpty())
{
IDETable inputTable = gpUtils.DecodeDETable(tableValue);
CheckForTableFields(inputTable, validateMessages.GetMessage(InputTable));
}
// Verify chosen reference_line_features has expected id field
gpParam = paramvalues.get_Element(ReferenceLineFeatures) as IGPParameter;
IGPValue featureClassValue = gpUtils.UnpackGPValue(gpParam);
if (!featureClassValue.IsEmpty())
{
IDETable inputTable = gpUtils.DecodeDETable(featureClassValue);
CheckForLinesIDField(inputTable, validateMessages.GetMessage(ReferenceLineFeatures));
}
return validateMessages;
}
public void Execute(IArray paramvalues, ITrackCancel trackcancel,
IGPEnvironmentManager envMgr, IGPMessages messages)
{
try
{
#region Validate our values, initialize utilities
IGPMessages validateMessages = Validate(paramvalues, false, envMgr);
if ((validateMessages as IGPMessage).IsError())
{
messages.AddError(1, "Validate failed");
return;
}
IGPUtilities gpUtils = new GPUtilitiesClass();
#endregion
#region Open input datasets (unpack values)
ITable inputTable;
IFeatureClass inputLineFeatures;
IGPParameter gpParam = paramvalues.get_Element(InputTable) as IGPParameter;
IGPValue inputTableValue = gpUtils.UnpackGPValue(gpParam);
IDataset dataset = gpUtils.OpenDataset(inputTableValue);
if (dataset != null)
inputTable = dataset as ITable;
else
{
messages.AddError(1, "Could not open input table.");
return;
}
gpParam = paramvalues.get_Element(ReferenceLineFeatures) as IGPParameter;
IGPValue inputFeaturesValue = gpUtils.UnpackGPValue(gpParam);
dataset = gpUtils.OpenDataset(inputFeaturesValue);
if (dataset != null)
inputLineFeatures = dataset as IFeatureClass;
else
{
messages.AddError(1, "Could not open input line features.");
return;
}
#endregion
#region Check for index
// check if streets table is indexed by ID and add a GPWarning message if not
IEnumIndex indexEnum = inputLineFeatures.Indexes.FindIndexesByFieldName(LinesIDFieldName);
indexEnum.Reset();
IIndex index;
while ((index = indexEnum.Next()) != null)
{
if (index.Fields.FieldCount != 1)
continue;
else
break;
}
if (index == null)
messages.AddWarning("Warning: " + LinesIDFieldName + " is not indexed.");
#endregion
// TODO: check if output exists and raise error or delete depending
// on overwrite outputs geoprocessing environment info
#region Create Output datasets
gpParam = paramvalues.get_Element(OutFeatureClassName) as IGPParameter;
IGPValue outputNameValue = gpUtils.UnpackGPValue(gpParam);
string outputName = (outputNameValue as IGPString).Value;
IFeatureClass outputSignsFeatureClass = SignpostUtilities.CreateSignsFeatureClass(inputLineFeatures, outputName);
gpParam = paramvalues.get_Element(OutStreetsTableName) as IGPParameter;
outputNameValue = gpUtils.UnpackGPValue(gpParam);
outputName = (outputNameValue as IGPString).Value;
ITable outputSignDetailTable = SignpostUtilities.CreateSignsDetailTable(inputLineFeatures, outputName);
#endregion
#region Populate data
PopulateData(inputTable, inputLineFeatures, outputSignsFeatureClass, outputSignDetailTable, messages, trackcancel);
#endregion
}
catch (COMException e)
{
messages.AddError(1, e.Message);
}
}
public string DisplayName
{
get
{
return "Import NAVSTREETS Signs";
}
}
public string MetadataFile
{
get
{
return "ImportNavStreetsSignsHelp.xml";
}
}
public IName FullName
{
get
{
IGPFunctionFactory functionFactory = new SignpostGPFunctionFactory();
return functionFactory.GetFunctionName(this.Name) as IName;
}
}
public bool IsLicensed()
{
return true;
}
public UID DialogCLSID
{
get
{
return null;
}
}
public string Name
{
get
{
return "ImportNavStreetsSigns";
}
}
public int HelpContext
{
get
{
return 0;
}
}
public string HelpFile
{
get
{
return null;
}
}
public object GetRenderer(IGPParameter gpParam)
{
return null;
}
#endregion
private bool CheckForTableFields(IDETable inputTable, IGPMessage gpMessage)
{
IFields fields = inputTable.Fields;
int fieldIndex;
for (int i = 0; i < FieldNames.Length - 1; i++)
{
fieldIndex = fields.FindField(FieldNames[i]);
if (fieldIndex == -1)
{
gpMessage.Type = esriGPMessageType.esriGPMessageTypeError;
gpMessage.Description = "Field named " + FieldNames[i] + " not found.";
return false;
}
if (fields.get_Field(fieldIndex).Type != FieldTypes[i])
{
gpMessage.Type = esriGPMessageType.esriGPMessageTypeError;
gpMessage.Description = "Field named " + FieldNames[i] + " is not the expected type.";
return false;
}
}
return true;
}
private bool CheckForLinesIDField(IDETable pInputTable, IGPMessage pMessage)
{
IFields fields = pInputTable.Fields;
int fieldIndex = fields.FindField(LinesIDFieldName);
if (fieldIndex == -1)
{
pMessage.Type = esriGPMessageType.esriGPMessageTypeError;
pMessage.Description = "Field named " + LinesIDFieldName + " not found.";
return false;
}
if (fields.get_Field(fieldIndex).Type != LinesIDFieldType)
{
pMessage.Type = esriGPMessageType.esriGPMessageTypeError;
pMessage.Description = "Field named " + LinesIDFieldName + " is not the expected type.";
return false;
}
return true;
}
private void PopulateData(ITable inputSignsTable, IFeatureClass inputLineFeatures,
IFeatureClass outputSignFeatures, ITable outputSignDetailTable,
IGPMessages messages, ITrackCancel trackcancel)
{
#region Find fields
//(Validate checked that these exist)
IFields inputTableFields = inputSignsTable.Fields;
int inExitIDFI = inputTableFields.FindField("SIGN_ID");
int inSequenceFI = inputTableFields.FindField("SEQ_NUM");
int inFromIDFI = inputTableFields.FindField("SRC_LINKID");
int inExitNumFI = inputTableFields.FindField("EXIT_NUM");
int inToIDFI = inputTableFields.FindField("DST_LINKID");
int inToNameFI = inputTableFields.FindField("SIGN_TEXT");
int inBranchRteIDFI = inputTableFields.FindField("BR_RTEID");
int inDirectionFI = inputTableFields.FindField("BR_RTEDIR");
int inToLocaleFI = inputTableFields.FindField("TOW_RTEID");
int inAccessFI = inputTableFields.FindField("SIGN_TXTTP");
// Find output fields (we just made these)
IFields outputSignFeatureFields = outputSignFeatures.Fields;
int outExitNameFI = outputSignFeatureFields.FindField("ExitName");
int[] outBranchXFI = new int[SignpostUtilities.MaxBranchCount];
int[] outBranchXDirFI = new int[SignpostUtilities.MaxBranchCount];
int[] outBranchXLngFI = new int[SignpostUtilities.MaxBranchCount];
int[] outTowardXFI = new int[SignpostUtilities.MaxBranchCount];
int[] outTowardXLngFI = new int[SignpostUtilities.MaxBranchCount];
string indexString;
for (int i = 0; i < SignpostUtilities.MaxBranchCount; i++)
{
indexString = Convert.ToString(i);
outBranchXFI[i] = outputSignFeatureFields.FindField("Branch" + indexString);
outBranchXDirFI[i] = outputSignFeatureFields.FindField("Branch" + indexString + "Dir");
outBranchXLngFI[i] = outputSignFeatureFields.FindField("Branch" + indexString + "Lng");
outTowardXFI[i] = outputSignFeatureFields.FindField("Toward" + indexString);
outTowardXLngFI[i] = outputSignFeatureFields.FindField("Toward" + indexString + "Lng");
}
IFields outputTableFields = outputSignDetailTable.Fields;
int outTblSignpostIDFI = outputTableFields.FindField("SignpostID");
int outTblSequenceFI = outputTableFields.FindField("Sequence");
int outTblEdgeFCIDFI = outputTableFields.FindField("EdgeFCID");
int outTblEdgeFIDFI = outputTableFields.FindField("EdgeFID");
int outTblEdgeFrmPosFI = outputTableFields.FindField("EdgeFrmPos");
int outTblEdgeToPosFI = outputTableFields.FindField("EdgeToPos");
// Find ID fields on referenced lines
int inLinesOIDFI = inputLineFeatures.FindField(inputLineFeatures.OIDFieldName);
int inLinesUserIDFI = inputLineFeatures.FindField(LinesIDFieldName);
int inLinesShapeFI = inputLineFeatures.FindField(inputLineFeatures.ShapeFieldName);
#endregion
// Fetch all line features referenced by the input signs table. We do the
// "join" this hard way to support all data sources in the sample.
// Also, for large numbers of sign records, this strategy of fetching all
// related features and holding them in RAM could be a problem. To fix
// this, one could process the input sign records in batches.
System.Collections.Hashtable lineFeaturesList = SignpostUtilities.FillFeatureCache(inputSignsTable, inFromIDFI, inToIDFI, inputLineFeatures, LinesIDFieldName, trackcancel);
// Create output feature/row buffers
IFeatureBuffer featureBuffer = outputSignFeatures.CreateFeatureBuffer();
IFeature feature = featureBuffer as IFeature;
IRowBuffer featureRowBuffer = featureBuffer as IRowBuffer;
IRowBuffer tableBuffer = outputSignDetailTable.CreateRowBuffer();
IRow row = tableBuffer as IRow;
IRowBuffer tableRowBuffer = tableBuffer as IRowBuffer;
// Create insert cursors.
IFeatureCursor featureInsertCursor = outputSignFeatures.Insert(true);
ICursor tableInsertCursor = outputSignDetailTable.Insert(true);
// Create input cursor for the signs table we are importing
ITableSort tableSort = new TableSortClass();
tableSort.Fields = "SIGN_ID, SEQ_NUM";
tableSort.set_Ascending("SIGN_ID", true);
tableSort.set_Ascending("SEQ_NUM", true);
tableSort.QueryFilter = null;
tableSort.Table = inputSignsTable;
tableSort.Sort(null);
ICursor inputCursor = tableSort.Rows;
IRow inputTableRow;
int numOutput = 0;
int numInput = 0;
int inSequenceValue;
long fromIDVal, toIDVal;
int nextBranchNum = -1, nextTowardNum = -1;
// these are initialized to prevent uninitialized variable compiler error
SignpostUtilities.FeatureData fromFeatureData = new SignpostUtilities.FeatureData(-1, null);
SignpostUtilities.FeatureData toFeatureData = new SignpostUtilities.FeatureData(-1, null);
object newOID;
string branchText, towardText, signText, accessText;
ICurve fromEdgeCurve, toEdgeCurve;
IPoint fromEdgeStart, fromEdgeEnd, toEdgeStart, toEdgeEnd;
int refLinesFCID = inputLineFeatures.ObjectClassID;
IGeometry outputSignGeometry;
double lastSignID = -1.0, currentSignID = -1.0;
double fromEdgeFromPos = 0.0;
double fromEdgeToPos = 1.0;
double toEdgeFromPos = 0.0;
double toEdgeToPos = 1.0;
while ((inputTableRow = inputCursor.NextRow()) != null)
{
currentSignID = Convert.ToInt32(inputTableRow.get_Value(inExitIDFI));
// If we have a new sign ID, we need to insert the signpost feature in progress
// and write the detail records.
// (identical code is also after the while loop for the last sign record)
if (currentSignID != lastSignID && lastSignID != -1)
{
// clean up unused parts of the row and pack toward/branch items
SignpostUtilities.CleanUpSignpostFeatureValues(featureBuffer, nextBranchNum - 1, nextTowardNum - 1,
outBranchXFI, outBranchXDirFI, outBranchXLngFI,
outTowardXFI, outTowardXLngFI);
// save sign feature record
newOID = featureInsertCursor.InsertFeature(featureBuffer);
// set streets table values
tableRowBuffer.set_Value(outTblSignpostIDFI, newOID);
tableRowBuffer.set_Value(outTblSequenceFI, 1);
tableRowBuffer.set_Value(outTblEdgeFCIDFI, refLinesFCID);
tableRowBuffer.set_Value(outTblEdgeFIDFI, fromFeatureData.OID);
tableRowBuffer.set_Value(outTblEdgeFrmPosFI, fromEdgeFromPos);
tableRowBuffer.set_Value(outTblEdgeToPosFI, fromEdgeToPos);
// insert first detail record
tableInsertCursor.InsertRow(tableRowBuffer);
tableRowBuffer.set_Value(outTblSequenceFI, 0);
tableRowBuffer.set_Value(outTblEdgeFIDFI, toFeatureData.OID);
tableRowBuffer.set_Value(outTblEdgeFrmPosFI, toEdgeFromPos);
tableRowBuffer.set_Value(outTblEdgeToPosFI, toEdgeToPos);
// insert second detail record
tableInsertCursor.InsertRow(tableRowBuffer);
numOutput++;
if ((numOutput % 100) == 0)
{
// check for user cancel
if (!trackcancel.Continue())
throw (new COMException("Function cancelled."));
}
}
lastSignID = currentSignID;
inSequenceValue = Convert.ToInt32(inputTableRow.get_Value(inSequenceFI));
if (inSequenceValue == 1)
{
// We are starting a sequence of records for a new sign.
// nextBranchNum and nextTowardNum keep track of which branch and
// toward item numbers we have used and are not necessarily the same
// as inSequenceValue.
nextBranchNum = 0;
nextTowardNum = 0;
fromIDVal = Convert.ToInt64(inputTableRow.get_Value(inFromIDFI));
toIDVal = Convert.ToInt64(inputTableRow.get_Value(inToIDFI));
// If the signpost references a line feature that is not in the lines
// feature class, add a warning message and keep going.
// Only warn for the first 100 not found.
numInput++;
try
{
fromFeatureData = (SignpostUtilities.FeatureData)lineFeaturesList[fromIDVal];
toFeatureData = (SignpostUtilities.FeatureData)lineFeaturesList[toIDVal];
}
catch
{
if (numInput - numOutput < 100)
{
messages.AddWarning("Line feature not found for sign with FromID: " +
Convert.ToString(fromIDVal) + ", ToID: " + Convert.ToString(toIDVal));
}
continue;
}
// To set from and to position in the detail table and to construct geometry
// for the output signs feature class, we need see where and
// if the two edge features connect to figure out their digitized direction.
fromEdgeCurve = fromFeatureData.feature as ICurve;
toEdgeCurve = toFeatureData.feature as ICurve;
fromEdgeStart = fromEdgeCurve.FromPoint;
fromEdgeEnd = fromEdgeCurve.ToPoint;
toEdgeStart = toEdgeCurve.FromPoint;
toEdgeEnd = toEdgeCurve.ToPoint;
fromEdgeFromPos = 0.0;
fromEdgeToPos = 1.0;
toEdgeFromPos = 0.0;
toEdgeToPos = 1.0;
// flip the from edge?
if (EqualPoints(fromEdgeStart, toEdgeStart) || EqualPoints(fromEdgeStart, toEdgeEnd))
{
fromEdgeFromPos = 1.0;
fromEdgeToPos = 0.0;
}
// flip the to edge?
if (EqualPoints(toEdgeEnd, fromEdgeStart) || EqualPoints(toEdgeEnd, fromEdgeEnd))
{
toEdgeFromPos = 1.0;
toEdgeToPos = 0.0;
}
// set sign feature values
// construct shape - the only purpose of the shape is visualization and it can be null
outputSignGeometry = MakeSignGeometry(fromEdgeCurve, toEdgeCurve, fromEdgeFromPos == 1.0, toEdgeFromPos == 1.0);
featureBuffer.Shape = outputSignGeometry;
featureBuffer.set_Value(outExitNameFI, inputTableRow.get_Value(inExitNumFI));
}
// Populate Branch items from BR_RTEID and BR_RTEDIR
branchText = (inputTableRow.get_Value(inBranchRteIDFI) as string).Trim();
if (branchText.Length > 0)
{
// check for schema overflow
if (nextBranchNum > SignpostUtilities.MaxBranchCount - 1)
continue;
// set values
featureBuffer.set_Value(outBranchXFI[nextBranchNum], branchText);
featureBuffer.set_Value(outBranchXDirFI[nextBranchNum], inputTableRow.get_Value(inDirectionFI));
featureBuffer.set_Value(outBranchXLngFI[nextBranchNum], "en");
// get ready for next branch
nextBranchNum++;
}
// Populate Branch or Toward items from SIGN_TEXT depending upon the value in the SIGN_TXTTP field:
// - if SIGN_TXTTP == "B" (direct), populate a branch
// - if SIGN_TXTTP == "T" (direct), populate a toward
signText = (inputTableRow.get_Value(inToNameFI) as string).Trim();
if (signText.Length > 0)
{
accessText = (inputTableRow.get_Value(inAccessFI) as string);
if (accessText == "B")
{
// check for schema overflow
if (nextBranchNum > SignpostUtilities.MaxBranchCount - 1)
continue;
// set values
featureBuffer.set_Value(outBranchXFI[nextBranchNum], signText);
featureBuffer.set_Value(outBranchXDirFI[nextBranchNum], inputTableRow.get_Value(inDirectionFI));
featureBuffer.set_Value(outBranchXLngFI[nextBranchNum], "en");
// get ready for next branch
nextBranchNum++;
}
else if (accessText == "T")
{
// check for schema overflow
if (nextTowardNum > SignpostUtilities.MaxBranchCount - 1)
continue;
// set values
featureBuffer.set_Value(outTowardXFI[nextTowardNum], signText);
featureBuffer.set_Value(outTowardXLngFI[nextTowardNum], "en");
// get ready for next toward
nextTowardNum++;
}
else
continue; // not expected
}
// Populate Toward items from TOW_RTEID
towardText = (inputTableRow.get_Value(inToLocaleFI) as string).Trim();
if (towardText.Length > 0)
{
// check for schema overflow
if (nextTowardNum > SignpostUtilities.MaxBranchCount - 1)
continue;
// set values
featureBuffer.set_Value(outTowardXFI[nextTowardNum], towardText);
featureBuffer.set_Value(outTowardXLngFI[nextTowardNum], "en");
// get ready for next toward
nextTowardNum++;
}
} // each input table record
// add the last signpost feature and detail records (same code as above)
// clean up unused parts of the row and pack toward/branch items
SignpostUtilities.CleanUpSignpostFeatureValues(featureBuffer, nextBranchNum - 1, nextTowardNum - 1,
outBranchXFI, outBranchXDirFI, outBranchXLngFI,
outTowardXFI, outTowardXLngFI);
// save sign feature record
newOID = featureInsertCursor.InsertFeature(featureBuffer);
// set streets table values
tableRowBuffer.set_Value(outTblSignpostIDFI, newOID);
tableRowBuffer.set_Value(outTblSequenceFI, 1);
tableRowBuffer.set_Value(outTblEdgeFCIDFI, refLinesFCID);
tableRowBuffer.set_Value(outTblEdgeFIDFI, fromFeatureData.OID);
tableRowBuffer.set_Value(outTblEdgeFrmPosFI, fromEdgeFromPos);
tableRowBuffer.set_Value(outTblEdgeToPosFI, fromEdgeToPos);
// insert first detail record
tableInsertCursor.InsertRow(tableRowBuffer);
tableRowBuffer.set_Value(outTblSequenceFI, 0);
tableRowBuffer.set_Value(outTblEdgeFIDFI, toFeatureData.OID);
tableRowBuffer.set_Value(outTblEdgeFrmPosFI, toEdgeFromPos);
tableRowBuffer.set_Value(outTblEdgeToPosFI, toEdgeToPos);
// insert second detail record
tableInsertCursor.InsertRow(tableRowBuffer);
numOutput++;
// add a summary message
messages.AddMessage(Convert.ToString(numOutput) + " of " + Convert.ToString(numInput) + " signposts added.");
return;
}
private bool EqualPoints(IPoint p1, IPoint p2)
{
return ((p1.X == p2.X) && (p1.Y == p2.Y));
}
private IGeometry MakeSignGeometry(ICurve fromEdgeCurve, ICurve toEdgeCurve,
bool reverseFromEdge, bool reverseToEdge)
{
ISegmentCollection resultSegments = new PolylineClass();
ICurve fromResultCurve, toResultCurve;
// add the part from the first line
if (reverseFromEdge)
{
fromEdgeCurve.GetSubcurve(0.0, 0.25, true, out fromResultCurve);
fromResultCurve.ReverseOrientation();
}
else
{
fromEdgeCurve.GetSubcurve(0.75, 1.0, true, out fromResultCurve);
}
resultSegments.AddSegmentCollection(fromResultCurve as ISegmentCollection);
// add the part from the second line
if (reverseToEdge)
{
toEdgeCurve.GetSubcurve(0.75, 1.0, true, out toResultCurve);
toResultCurve.ReverseOrientation();
}
else
{
toEdgeCurve.GetSubcurve(0.0, 0.25, true, out toResultCurve);
}
resultSegments.AddSegmentCollection(toResultCurve as ISegmentCollection);
return resultSegments as IGeometry;
}
}
}
[Visual Basic .NET]
ImportNavStreetsSignsFunction.vb
Imports System
Imports System.Runtime.InteropServices
Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.Geoprocessing
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.Carto
Namespace GPImportSignpostFunctions
<Guid("B6CA7AB9-F065-487f-8399-5DF7B9D0787B")> _
<ClassInterface(ClassInterfaceType.None)> _
<ProgId("GPImportSignpostFunctions.ImportNavStreetsSignsFunction")> _
Public Class ImportNavStreetsSignsFunction
Implements IGPFunction
#Region "Constants"
' parameter index constants
Private Const InputTable As Integer = 0
Private Const ReferenceLineFeatures As Integer = 1
Private Const OutFeatureClassName As Integer = 2
Private Const OutStreetsTableName As Integer = 3
' field names and types
Private Shared ReadOnly FieldNames() As String = New String() _
{"SRC_LINKID", "DST_LINKID", "SIGN_ID", "SEQ_NUM", "EXIT_NUM", _
"BR_RTEID", "BR_RTEDIR", "SIGN_TXTTP", "SIGN_TEXT", "LANG_CODE", _
"TOW_RTEID", "SIGN_TXTTP"}
Private Shared ReadOnly FieldTypes() As esriFieldType = New esriFieldType() _
{esriFieldType.esriFieldTypeDouble, _
esriFieldType.esriFieldTypeDouble, _
esriFieldType.esriFieldTypeDouble, _
esriFieldType.esriFieldTypeSmallInteger, _
esriFieldType.esriFieldTypeString, _
esriFieldType.esriFieldTypeString, _
esriFieldType.esriFieldTypeString, _
esriFieldType.esriFieldTypeString, _
esriFieldType.esriFieldTypeString, _
esriFieldType.esriFieldTypeString, _
esriFieldType.esriFieldTypeString, _
esriFieldType.esriFieldTypeString}
Private Shared ReadOnly LinesIDFieldName As String = "LINK_ID"
Private Shared ReadOnly LinesIDFieldType As esriFieldType = esriFieldType.esriFieldTypeDouble
#End Region
Private m_parameters As IArray
Public Sub New()
End Sub
#Region "IGPFunction Members"
Public ReadOnly Property ParameterInfo() As IArray Implements IGPFunction.ParameterInfo
Get
Dim gpParamArray As IArray = New ESRI.ArcGIS.esriSystem.Array
' 1 - input_signs_table
Dim paramEdit As IGPParameterEdit = New GPParameter
paramEdit.DataType = New DETableType
paramEdit.Value = New DETable
paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput
paramEdit.DisplayName = "Input Signs Table"
paramEdit.Enabled = True
paramEdit.Name = "input_signs_table"
paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired
gpParamArray.Add(paramEdit)
' 2 - reference_street_features
paramEdit = New GPParameter
paramEdit.DataType = New DEFeatureClassType
paramEdit.Value = New DEFeatureClass
paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput
paramEdit.DisplayName = "Input Street Features"
paramEdit.Enabled = True
paramEdit.Name = "reference_street_features"
paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired
Dim lineFeatureClassDomain As IGPFeatureClassDomain = New GPFeatureClassDomain
lineFeatureClassDomain.AddType(esriGeometryType.esriGeometryLine)
lineFeatureClassDomain.AddType(esriGeometryType.esriGeometryPolyline)
paramEdit.Domain = CType(lineFeatureClassDomain, IGPDomain)
gpParamArray.Add(paramEdit)
' 3 - out_feature_class_name
paramEdit = New GPParameter
paramEdit.DataType = New GPStringType
Dim stringVal As IGPString = New GPString
stringVal.Value = "Signpost"
paramEdit.Value = CType(stringVal, IGPValue)
paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput
paramEdit.DisplayName = "Output Signpost Feature Class Name"
paramEdit.Enabled = True
paramEdit.Name = "out_feature_class_name"
paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired
gpParamArray.Add(paramEdit)
' 4 - out_table_name
paramEdit = New GPParameter
paramEdit.DataType = New GPStringType
stringVal = New GPString
stringVal.Value = "SignpostSt"
paramEdit.Value = CType(stringVal, IGPValue)
paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput
paramEdit.DisplayName = "Output Signpost Streets Table Name"
paramEdit.Enabled = True
paramEdit.Name = "out_streets_table_name"
paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired
gpParamArray.Add(paramEdit)
' TODO: add two derived output parameters for chaining this function
' in models
Return gpParamArray
End Get
End Property
Public Function Validate(ByVal paramvalues As IArray, ByVal updateValues As Boolean, ByVal envMgr As IGPEnvironmentManager) As IGPMessages Implements IGPFunction.Validate
' Create the GPUtilities Object
Dim gpUtils As IGPUtilities = New GPUtilities
' Initialize a copy of our parameters
If m_parameters Is Nothing Then
m_parameters = ParameterInfo
End If
' Call InternalValidate to check for required parameters
Dim validateMessages As IGPMessages = gpUtils.InternalValidate(m_parameters, paramvalues, updateValues, True, envMgr)
' Verify chosen input table has the expected fields
Dim gpParam As IGPParameter = CType(paramvalues.Element(InputTable), IGPParameter)
Dim tableValue As IGPValue = gpUtils.UnpackGPValue(gpParam)
' CheckForTableFields will report errors by modifying the relevant GPMessage
If Not tableValue.IsEmpty() Then
Dim inputDETable As IDETable = gpUtils.DecodeDETable(tableValue)
CheckForTableFields(inputDETable, validateMessages.GetMessage(InputTable))
End If
' Verify chosen reference_line_features has expected id field
gpParam = CType(paramvalues.Element(ReferenceLineFeatures), IGPParameter)
Dim featureClassValue As IGPValue = gpUtils.UnpackGPValue(gpParam)
If Not featureClassValue.IsEmpty() Then
Dim inputDETable As IDETable = gpUtils.DecodeDETable(featureClassValue)
CheckForLinesIDField(inputDETable, validateMessages.GetMessage(ReferenceLineFeatures))
End If
Return validateMessages
End Function
Public Sub Execute(ByVal paramvalues As IArray, ByVal trackcancel As ITrackCancel, ByVal envMgr As IGPEnvironmentManager, ByVal messages As IGPMessages) Implements IGPFunction.Execute
Try
' VALIDATE OUR VALUES, INITIALIZE UTILITIES
Dim validateMessages As IGPMessages = Validate(paramvalues, False, envMgr)
If CType(validateMessages, IGPMessage).IsError() Then
messages.AddError(1, "Validate failed")
Return
End If
Dim gpUtils As IGPUtilities = New GPUtilities
' OPEN INPUT DATASETS (UNPACK VALUES)
Dim inputTableAsITable As ITable
Dim inputLineFeatures As IFeatureClass
Dim gpParam As IGPParameter = CType(paramvalues.Element(InputTable), IGPParameter)
Dim inputTableValue As IGPValue = gpUtils.UnpackGPValue(gpParam)
Dim dataset As IDataset = gpUtils.OpenDataset(inputTableValue)
If dataset IsNot Nothing Then
inputTableAsITable = CType(dataset, ITable)
Else
messages.AddError(1, "Could not open input table.")
Return
End If
gpParam = CType(paramvalues.Element(ReferenceLineFeatures), IGPParameter)
Dim inputFeaturesValue As IGPValue = gpUtils.UnpackGPValue(gpParam)
dataset = gpUtils.OpenDataset(inputFeaturesValue)
If dataset IsNot Nothing Then
inputLineFeatures = CType(dataset, IFeatureClass)
Else
messages.AddError(1, "Could not open input line features.")
Return
End If
' CHECK FOR INDEX
' check if streets table is indexed by ID and add a GPWarning message if not
Dim indexEnum As IEnumIndex = inputLineFeatures.Indexes.FindIndexesByFieldName(LinesIDFieldName)
indexEnum.Reset()
Dim index As IIndex = indexEnum.Next()
While index IsNot Nothing
If index.Fields.FieldCount <> 1 Then
Continue While
Else
Exit While
End If
index = indexEnum.Next()
End While
If index Is Nothing Then
messages.AddWarning("Warning: " + LinesIDFieldName + " is not indexed.")
End If
' TODO: check if output exists and raise error or delete depending
' on overwrite outputs geoprocessing environment info
' CREATE OUTPUT DATASETS
gpParam = CType(paramvalues.Element(OutFeatureClassName), IGPParameter)
Dim outputNameValue As IGPValue = gpUtils.UnpackGPValue(gpParam)
Dim outputName As String = CType(outputNameValue, IGPString).Value
Dim outputSignsFeatureClass As IFeatureClass = SignpostUtilities.CreateSignsFeatureClass(inputLineFeatures, outputName)
gpParam = CType(paramvalues.Element(OutStreetsTableName), IGPParameter)
outputNameValue = gpUtils.UnpackGPValue(gpParam)
outputName = CType(outputNameValue, IGPString).Value
Dim outputSignDetailTable As ITable = SignpostUtilities.CreateSignsDetailTable(inputLineFeatures, outputName)
' POPULATE DATA
PopulateData(inputTableAsITable, inputLineFeatures, outputSignsFeatureClass, outputSignDetailTable, messages, trackcancel)
Catch e As COMException
messages.AddError(1, e.Message)
End Try
End Sub
Public ReadOnly Property DisplayName() As String Implements IGPFunction.DisplayName
Get
Return "Import NAVSTREETS Signs"
End Get
End Property
Public ReadOnly Property MetadataFile() As String Implements IGPFunction.MetadataFile
Get
Return "ImportNavStreetsSignsHelp.xml"
End Get
End Property
Public ReadOnly Property FullName() As IName Implements IGPFunction.FullName
Get
Dim functionFactory As IGPFunctionFactory = New SignpostGPFunctionFactory
Return CType(functionFactory.GetFunctionName(Me.Name), IName)
End Get
End Property
Public Function IsLicensed() As Boolean Implements IGPFunction.IsLicensed
Return True
End Function
Public ReadOnly Property DialogCLSID() As UID Implements IGPFunction.DialogCLSID
Get
Return Nothing
End Get
End Property
Public ReadOnly Property Name() As String Implements IGPFunction.Name
Get
Return "ImportNavStreetsSigns"
End Get
End Property
Public ReadOnly Property HelpContext() As Integer Implements IGPFunction.HelpContext
Get
Return 0
End Get
End Property
Public ReadOnly Property HelpFile() As String Implements IGPFunction.HelpFile
Get
Return Nothing
End Get
End Property
Public Function GetRenderer(ByVal pParam As IGPParameter) As Object Implements IGPFunction.GetRenderer
Return Nothing
End Function
#End Region
Private Function CheckForTableFields(ByVal inputDETable As IDETable, ByVal gpMessage As IGPMessage) As Boolean
Dim fields As IFields = inputDETable.Fields
Dim fieldIndex As Integer
For i As Integer = 0 To FieldNames.Length - 2
fieldIndex = fields.FindField(FieldNames(i))
If fieldIndex = -1 Then
gpMessage.Type = esriGPMessageType.esriGPMessageTypeError
gpMessage.Description = "Field named " + FieldNames(i) + " not found."
Return False
End If
If fields.Field(fieldIndex).Type <> FieldTypes(i) Then
gpMessage.Type = esriGPMessageType.esriGPMessageTypeError
gpMessage.Description = "Field named " + FieldNames(i) + " is not the expected type."
Return False
End If
Next i
Return True
End Function
Private Function CheckForLinesIDField(ByVal inputDETable As IDETable, ByVal gpMessage As IGPMessage) As Boolean
Dim fields As IFields = inputDETable.Fields
Dim fieldIndex As Integer = fields.FindField(LinesIDFieldName)
If fieldIndex = -1 Then
gpMessage.Type = esriGPMessageType.esriGPMessageTypeError
gpMessage.Description = "Field named " + LinesIDFieldName + " not found."
Return False
End If
If fields.Field(fieldIndex).Type <> LinesIDFieldType Then
gpMessage.Type = esriGPMessageType.esriGPMessageTypeError
gpMessage.Description = "Field named " + LinesIDFieldName + " is not the expected type."
Return False
End If
Return True
End Function
Private Sub PopulateData(ByVal inputSignsTable As ITable, ByVal inputLineFeatures As IFeatureClass, _
ByVal outputSignFeatures As IFeatureClass, ByVal outputSignDetailTable As ITable, _
ByVal messages As IGPMessages, ByVal trackcancel As ITrackCancel)
'FIND FIELDS
'(Validate checked that these exist)
Dim inputTableFields As IFields = inputSignsTable.Fields
Dim inExitIDFI As Integer = inputTableFields.FindField("SIGN_ID")
Dim inSequenceFI As Integer = inputTableFields.FindField("SEQ_NUM")
Dim inFromIDFI As Integer = inputTableFields.FindField("SRC_LINKID")
Dim inExitNumFI As Integer = inputTableFields.FindField("EXIT_NUM")
Dim inToIDFI As Integer = inputTableFields.FindField("DST_LINKID")
Dim inToNameFI As Integer = inputTableFields.FindField("SIGN_TEXT")
Dim inBranchRteIDFI As Integer = inputTableFields.FindField("BR_RTEID")
Dim inDirectionFI As Integer = inputTableFields.FindField("BR_RTEDIR")
Dim inToLocaleFI As Integer = inputTableFields.FindField("TOW_RTEID")
Dim inAccessFI As Integer = inputTableFields.FindField("SIGN_TXTTP")
' Find output fields (we just made these)
Dim outputSignFeatureFields As IFields = outputSignFeatures.Fields
Dim outExitNameFI As Integer = outputSignFeatureFields.FindField("ExitName")
Dim outBranchXFI() As Integer = New Integer(SignpostUtilities.MaxBranchCount) {}
Dim outBranchXDirFI() As Integer = New Integer(SignpostUtilities.MaxBranchCount) {}
Dim outBranchXLngFI() As Integer = New Integer(SignpostUtilities.MaxBranchCount) {}
Dim outTowardXFI() As Integer = New Integer(SignpostUtilities.MaxBranchCount) {}
Dim outTowardXLngFI() As Integer = New Integer(SignpostUtilities.MaxBranchCount) {}
Dim indexString As String
For i As Integer = 0 To SignpostUtilities.MaxBranchCount - 1
indexString = Convert.ToString(i)
outBranchXFI(i) = outputSignFeatureFields.FindField("Branch" + indexString)
outBranchXDirFI(i) = outputSignFeatureFields.FindField("Branch" + indexString + "Dir")
outBranchXLngFI(i) = outputSignFeatureFields.FindField("Branch" + indexString + "Lng")
outTowardXFI(i) = outputSignFeatureFields.FindField("Toward" + indexString)
outTowardXLngFI(i) = outputSignFeatureFields.FindField("Toward" + indexString + "Lng")
Next i
Dim outputTableFields As IFields = outputSignDetailTable.Fields
Dim outTblSignpostIDFI As Integer = outputTableFields.FindField("SignpostID")
Dim outTblSequenceFI As Integer = outputTableFields.FindField("Sequence")
Dim outTblEdgeFCIDFI As Integer = outputTableFields.FindField("EdgeFCID")
Dim outTblEdgeFIDFI As Integer = outputTableFields.FindField("EdgeFID")
Dim outTblEdgeFrmPosFI As Integer = outputTableFields.FindField("EdgeFrmPos")
Dim outTblEdgeToPosFI As Integer = outputTableFields.FindField("EdgeToPos")
' Find ID fields on referenced lines
Dim inLinesOIDFI As Integer = inputLineFeatures.FindField(inputLineFeatures.OIDFieldName)
Dim inLinesUserIDFI As Integer = inputLineFeatures.FindField(LinesIDFieldName)
Dim inLinesShapeFI As Integer = inputLineFeatures.FindField(inputLineFeatures.ShapeFieldName)
' Fetch all line features referenced by the input signs table. We do the
' "join" this hard way to support all data sources in the sample.
' Also, for large numbers of sign records, this strategy of fetching all
' related features and holding them in RAM could be a problem. To fix
' this, one could process the input sign records in batches.
Dim lineFeaturesList As System.Collections.Hashtable = SignpostUtilities.FillFeatureCache(inputSignsTable, inFromIDFI, inToIDFI, inputLineFeatures, LinesIDFieldName, trackcancel)
' Create output feature/row buffers
Dim featureBuffer As IFeatureBuffer = outputSignFeatures.CreateFeatureBuffer()
Dim feature As IFeature = CType(featureBuffer, IFeature)
Dim featureRowBuffer As IRowBuffer = featureBuffer
Dim tableBuffer As IRowBuffer = outputSignDetailTable.CreateRowBuffer()
Dim row As IRow = CType(tableBuffer, IRow)
Dim tableRowBuffer As IRowBuffer = tableBuffer
' Create insert cursors.
Dim featureInsertCursor As IFeatureCursor = outputSignFeatures.Insert(True)
Dim tableInsertCursor As ICursor = outputSignDetailTable.Insert(True)
' Create input cursor for the signs table we are importing
Dim tableSort As ITableSort = New TableSort
tableSort.Fields = "SIGN_ID, SEQ_NUM"
tableSort.Ascending("SIGN_ID") = True
tableSort.Ascending("SEQ_NUM") = True
tableSort.QueryFilter = Nothing
tableSort.Table = inputSignsTable
tableSort.Sort(Nothing)
Dim inputCursor As ICursor = tableSort.Rows
Dim inputTableRow As IRow
Dim numInput As Integer = 0
Dim numOutput As Integer = 0
Dim inSequenceValue As Integer
Dim fromIDVal As Long, toIDVal As Long
Dim nextBranchNum As Integer = -1, nextTowardNum As Integer = -1
' these are initialized to prevent uninitialized variable compiler error
Dim fromFeatureData As SignpostUtilities.FeatureData = New SignpostUtilities.FeatureData(-1, Nothing)
Dim toFeatureData As SignpostUtilities.FeatureData = New SignpostUtilities.FeatureData(-1, Nothing)
Dim newOID As Object
Dim branchText As String, towardText As String, signText As String, accessText As String
Dim fromEdgeCurve As ICurve, toEdgeCurve As ICurve
Dim fromEdgeStart As IPoint, fromEdgeEnd As IPoint, toEdgeStart As IPoint, toEdgeEnd As IPoint
Dim refLinesFCID As Integer = inputLineFeatures.ObjectClassID
Dim outputSignGeometry As IGeometry
Dim lastSignID As Double = -1.0, currentSignID As Double = -1.0
Dim fromEdgeFromPos As Double = 0.0
Dim fromEdgeToPos As Double = 1.0
Dim toEdgeFromPos As Double = 0.0
Dim toEdgeToPos As Double = 1.0
inputTableRow = inputCursor.NextRow()
While inputTableRow IsNot Nothing
currentSignID = Convert.ToInt32(inputTableRow.Value(inExitIDFI))
' If we have a new sign ID, we need to insert the signpost feature in progress
' and write the detail records.
' (identical code is also after the while loop for the last sign record)
If currentSignID <> lastSignID And lastSignID <> -1 Then
' clean up unused parts of the row and pack toward/branch items
SignpostUtilities.CleanUpSignpostFeatureValues(featureBuffer, nextBranchNum - 1, nextTowardNum - 1, _
outBranchXFI, outBranchXDirFI, outBranchXLngFI, _
outTowardXFI, outTowardXLngFI)
' save sign feature record
newOID = featureInsertCursor.InsertFeature(featureBuffer)
' set streets table values
tableRowBuffer.Value(outTblSignpostIDFI) = newOID
tableRowBuffer.Value(outTblSequenceFI) = 1
tableRowBuffer.Value(outTblEdgeFCIDFI) = refLinesFCID
tableRowBuffer.Value(outTblEdgeFIDFI) = fromFeatureData.OID
tableRowBuffer.Value(outTblEdgeFrmPosFI) = fromEdgeFromPos
tableRowBuffer.Value(outTblEdgeToPosFI) = fromEdgeToPos
' insert first detail record
tableInsertCursor.InsertRow(tableRowBuffer)
tableRowBuffer.Value(outTblSequenceFI) = 0
tableRowBuffer.Value(outTblEdgeFIDFI) = toFeatureData.OID
tableRowBuffer.Value(outTblEdgeFrmPosFI) = toEdgeFromPos
tableRowBuffer.Value(outTblEdgeToPosFI) = toEdgeToPos
' insert second detail record
tableInsertCursor.InsertRow(tableRowBuffer)
numOutput += 1
If (numOutput Mod 100) = 0 Then
' check for user cancel
If Not trackcancel.Continue() Then
Throw (New COMException("Function cancelled."))
End If
End If
End If
lastSignID = currentSignID
inSequenceValue = Convert.ToInt32(inputTableRow.Value(inSequenceFI))
If inSequenceValue = 1 Then
' We are starting a sequence of records for a new sign.
' nextBranchNum and nextTowardNum keep track of which branch and
' toward item numbers we have used and are not necessarily the same
' as inSequenceValue.
nextBranchNum = 0
nextTowardNum = 0
fromIDVal = Convert.ToInt64(inputTableRow.Value(inFromIDFI))
toIDVal = Convert.ToInt64(inputTableRow.Value(inToIDFI))
' If the signpost references a line feature that is not in the lines
' feature class, add a warning message and keep going.
' Only warn for the first 100 not found.
numInput += 1
Try
fromFeatureData = CType(lineFeaturesList(fromIDVal), SignpostUtilities.FeatureData)
toFeatureData = CType(lineFeaturesList(toIDVal), SignpostUtilities.FeatureData)
Catch ex As Exception
If (numInput - numOutput < 100) Then
messages.AddWarning("Line feature not found for sign with FromID: " + _
Convert.ToString(fromIDVal) + ", ToID: " + Convert.ToString(toIDVal))
End If
inputTableRow = inputCursor.NextRow()
Continue While
End Try
' To set from and to position in the detail table and to construct geometry
' for the output signs feature class, we need see where and
' if the two edge features connect to figure out their digitized direction.
fromEdgeCurve = CType(fromFeatureData.feature, ICurve)
toEdgeCurve = CType(toFeatureData.feature, ICurve)
fromEdgeStart = fromEdgeCurve.FromPoint
fromEdgeEnd = fromEdgeCurve.ToPoint
toEdgeStart = toEdgeCurve.FromPoint
toEdgeEnd = toEdgeCurve.ToPoint
fromEdgeFromPos = 0.0
fromEdgeToPos = 1.0
toEdgeFromPos = 0.0
toEdgeToPos = 1.0
' flip the from edge?
If EqualPoints(fromEdgeStart, toEdgeStart) Or EqualPoints(fromEdgeStart, toEdgeEnd) Then
fromEdgeFromPos = 1.0
fromEdgeToPos = 0.0
End If
' flip the to edge?
If EqualPoints(toEdgeEnd, fromEdgeStart) Or EqualPoints(toEdgeEnd, fromEdgeEnd) Then
toEdgeFromPos = 1.0
toEdgeToPos = 0.0
End If
' set sign feature values
' construct shape - the only purpose of the shape is visualization and it can be null
outputSignGeometry = MakeSignGeometry(fromEdgeCurve, toEdgeCurve, fromEdgeFromPos = 1.0, toEdgeFromPos = 1.0)
featureBuffer.Shape = outputSignGeometry
featureBuffer.Value(outExitNameFI) = inputTableRow.Value(inExitNumFI)
End If
' Populate Branch items from BR_RTEID and BR_RTEDIR
branchText = CType(inputTableRow.Value(inBranchRteIDFI), String).Trim()
If branchText.Length > 0 Then
' check for schema overflow
If nextBranchNum > SignpostUtilities.MaxBranchCount - 1 Then
inputTableRow = inputCursor.NextRow()
Continue While
End If
' set values
featureBuffer.Value(outBranchXFI(nextBranchNum)) = branchText
featureBuffer.Value(outBranchXDirFI(nextBranchNum)) = inputTableRow.Value(inDirectionFI)
featureBuffer.Value(outBranchXLngFI(nextBranchNum)) = "en"
' get ready for next branch
nextBranchNum += 1
End If
' Populate Branch or Toward items from SIGN_TEXT depending upon the value in the SIGN_TXTTP field:
' - if SIGN_TXTTP == "B" (direct), populate a branch
' - if SIGN_TXTTP == "T" (direct), populate a toward
signText = CType(inputTableRow.Value(inToNameFI), String).Trim()
If signText.Length > 0 Then
accessText = CStr(inputTableRow.Value(inAccessFI))
If accessText = "B" Then
' check for schema overflow
If nextBranchNum > SignpostUtilities.MaxBranchCount - 1 Then
inputTableRow = inputCursor.NextRow()
Continue While
End If
' set values
featureBuffer.Value(outBranchXFI(nextBranchNum)) = signText
featureBuffer.Value(outBranchXDirFI(nextBranchNum)) = inputTableRow.Value(inDirectionFI)
featureBuffer.Value(outBranchXLngFI(nextBranchNum)) = "en"
' get ready for next branch
nextBranchNum += 1
ElseIf accessText = "T" Then
' check for schema overflow
If nextTowardNum > SignpostUtilities.MaxBranchCount - 1 Then
inputTableRow = inputCursor.NextRow()
Continue While
End If
' set values
featureBuffer.Value(outTowardXFI(nextTowardNum)) = signText
featureBuffer.Value(outTowardXLngFI(nextTowardNum)) = "en"
' get ready for next toward
nextTowardNum += 1
Else
inputTableRow = inputCursor.NextRow()
Continue While ' not expected
End If
End If
' Populate Toward items from TOW_RTEID
towardText = CType(inputTableRow.Value(inToLocaleFI), String).Trim()
If towardText.Length > 0 Then
' check for schema overflow
If nextTowardNum > SignpostUtilities.MaxBranchCount - 1 Then
inputTableRow = inputCursor.NextRow()
Continue While
End If
' set values
featureBuffer.Value(outTowardXFI(nextTowardNum)) = towardText
featureBuffer.Value(outTowardXLngFI(nextTowardNum)) = "en"
' get ready for next toward
nextTowardNum += 1
End If
inputTableRow = inputCursor.NextRow()
End While
' each input table record
' add the last signpost feature and detail records (same code as above)
' clean up unused parts of the row and pack toward/branch items
SignpostUtilities.CleanUpSignpostFeatureValues(featureBuffer, nextBranchNum - 1, nextTowardNum - 1, _
outBranchXFI, outBranchXDirFI, outBranchXLngFI, _
outTowardXFI, outTowardXLngFI)
' save sign feature record
newOID = featureInsertCursor.InsertFeature(featureBuffer)
' set streets table values
tableRowBuffer.Value(outTblSignpostIDFI) = newOID
tableRowBuffer.Value(outTblSequenceFI) = 1
tableRowBuffer.Value(outTblEdgeFCIDFI) = refLinesFCID
tableRowBuffer.Value(outTblEdgeFIDFI) = fromFeatureData.OID
tableRowBuffer.Value(outTblEdgeFrmPosFI) = fromEdgeFromPos
tableRowBuffer.Value(outTblEdgeToPosFI) = fromEdgeToPos
' insert first detail record
tableInsertCursor.InsertRow(tableRowBuffer)
tableRowBuffer.Value(outTblSequenceFI) = 0
tableRowBuffer.Value(outTblEdgeFIDFI) = toFeatureData.OID
tableRowBuffer.Value(outTblEdgeFrmPosFI) = toEdgeFromPos
tableRowBuffer.Value(outTblEdgeToPosFI) = toEdgeToPos
' insert second detail record
tableInsertCursor.InsertRow(tableRowBuffer)
numOutput += 1
' add a summary message
messages.AddMessage(Convert.ToString(numOutput) + " of " + Convert.ToString(numInput) + " signposts added.")
Return
End Sub
Private Function EqualPoints(ByVal p1 As IPoint, ByVal p2 As IPoint) As Boolean
Return ((p1.X = p2.X) And (p1.Y = p2.Y))
End Function
Private Function MakeSignGeometry(ByVal fromEdgeCurve As ICurve, ByVal toEdgeCurve As ICurve, _
ByVal reverseFromEdge As Boolean, ByVal reverseToEdge As Boolean) As IGeometry
Dim resultSegments As ISegmentCollection = New Polyline
Dim fromResultCurve As ICurve, toResultCurve As ICurve
' add the part from the first line
If reverseFromEdge Then
fromEdgeCurve.GetSubcurve(0.0, 0.25, True, fromResultCurve)
fromResultCurve.ReverseOrientation()
Else
fromEdgeCurve.GetSubcurve(0.75, 1.0, True, fromResultCurve)
End If
resultSegments.AddSegmentCollection(CType(fromResultCurve, ISegmentCollection))
' add the part from the second line
If reverseToEdge Then
toEdgeCurve.GetSubcurve(0.75, 1.0, True, toResultCurve)
toResultCurve.ReverseOrientation()
Else
toEdgeCurve.GetSubcurve(0.0, 0.25, True, toResultCurve)
End If
resultSegments.AddSegmentCollection(CType(toResultCurve, ISegmentCollection))
Return CType(resultSegments, IGeometry)
End Function
End Class
End Namespace