ImportDynamapSignsFunction.vb
' 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. ' Imports System Imports System.Runtime.InteropServices Imports ESRI.ArcGIS.esriSystem Imports ESRI.ArcGIS.Geodatabase Imports ESRI.ArcGIS.Geoprocessing Imports ESRI.ArcGIS.Geometry Namespace GPImportSignpostFunctions <Guid("3C5D851A-A98F-4bd4-911C-3296B094DDE8")> _ <ClassInterface(ClassInterfaceType.None)> _ <ProgId("GPImportSignpostFunctions.ImportDynamapSignsFunction")> _ Public Class ImportDynamapSignsFunction 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() _ {"EXIT_ID", "SEQUENCE", "FROM_ID", "FROM_NAME", "EXIT_NUM", _ "TO_ID", "TO_NAME", "SHIELD", "HWY_NUM", "DIRECTION", "TO_LOCALE", _ "ACCESS", "EXIT_ONLY", "LONGITUDE", "LATITUDE"} Private Shared ReadOnly FieldTypes() As esriFieldType = New esriFieldType() _ {esriFieldType.esriFieldTypeDouble, _ esriFieldType.esriFieldTypeSmallInteger, _ esriFieldType.esriFieldTypeDouble, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeDouble, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeDouble, _ esriFieldType.esriFieldTypeDouble} Private Shared ReadOnly LinesIDFieldName As String = "Dynamap_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_highway_sign_features Dim paramEdit As IGPParameterEdit = New GPParameter paramEdit.DataType = New DETableType paramEdit.Value = New DETable paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput paramEdit.DisplayName = "Input Highway Sign Features" paramEdit.Enabled = True paramEdit.Name = "input_highway_sign_features" 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 Dynamap Signs" End Get End Property Public ReadOnly Property MetadataFile() As String Implements IGPFunction.MetadataFile Get Return "ImportDynamapSignsHelp.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 "ImportDynamapSigns" 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 gpParam 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("EXIT_ID") Dim inSequenceFI As Integer = inputTableFields.FindField("SEQUENCE") Dim inFromIDFI As Integer = inputTableFields.FindField("FROM_ID") Dim inExitNumFI As Integer = inputTableFields.FindField("EXIT_NUM") Dim inToIDFI As Integer = inputTableFields.FindField("TO_ID") Dim inToNameFI As Integer = inputTableFields.FindField("TO_NAME") Dim inDirectionFI As Integer = inputTableFields.FindField("DIRECTION") Dim inToLocaleFI As Integer = inputTableFields.FindField("TO_LOCALE") Dim inAccessFI As Integer = inputTableFields.FindField("ACCESS") ' 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 = "EXIT_ID, SEQUENCE" tableSort.Ascending("EXIT_ID") = True tableSort.Ascending("SEQUENCE") = 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, accessVal As Object Dim outputText As String, outputDirText 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) = 2 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 or toward item depending upon the value in the ACCESS field: ' if ACCESS == "D" (direct), populate branch(s) ' if ACCESS == "I" (direct), populate a toward(s) accessVal = inputTableRow.Value(inAccessFI) If CType(accessVal, String) = "D" Then ' check for schema overflow If nextBranchNum > SignpostUtilities.MaxBranchCount - 1 Then inputTableRow = inputCursor.NextRow() Continue While End If outputText = CType(inputTableRow.Value(inToNameFI), String).Trim() If outputText.Length > 0 Then ' set values featureBuffer.Value(outBranchXFI(nextBranchNum)) = outputText featureBuffer.Value(outBranchXDirFI(nextBranchNum)) = inputTableRow.Value(inDirectionFI) featureBuffer.Value(outBranchXLngFI(nextBranchNum)) = "en" ' get ready for next branch nextBranchNum += 1 End If ' there are rare cases when we'll have Access == D (TO_NAME) AND data for TO_LOCALE outputText = CType(inputTableRow.Value(inToLocaleFI), String).Trim() If outputText.Length > 0 Then ' set values featureBuffer.Value(outBranchXFI(nextBranchNum)) = outputText featureBuffer.Value(outBranchXDirFI(nextBranchNum)) = Nothing featureBuffer.Value(outBranchXLngFI(nextBranchNum)) = "en" ' get ready for next branch nextBranchNum += 1 End If ElseIf CType(accessVal, String) = "I" Then ' check for schema overflow If nextTowardNum > SignpostUtilities.MaxBranchCount - 1 Then inputTableRow = inputCursor.NextRow() Continue While End If outputText = CType(inputTableRow.Value(inToNameFI), String).Trim() If outputText.Length > 0 Then outputDirText = CType(inputTableRow.Value(inDirectionFI), String).Trim() If outputDirText.Length > 0 Then outputText += " " outputText += outputDirText End If ' set values featureBuffer.Value(outTowardXFI(nextTowardNum)) = outputText featureBuffer.Value(outTowardXLngFI(nextTowardNum)) = "en" ' get ready for next toward nextTowardNum += 1 End If ' there are rare cases when we'll have Access == I (TO_LOCALE) AND data for TO_NAME outputText = CType(inputTableRow.Value(inToLocaleFI), String).Trim() If outputText.Length > 0 Then ' set values featureBuffer.Value(outTowardXFI(nextTowardNum)) = outputText featureBuffer.Value(outTowardXLngFI(nextTowardNum)) = "en" ' get ready for next toward nextTowardNum += 1 End If Else inputTableRow = inputCursor.NextRow() Continue While ' not expected 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 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) = 2 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