About the Import signposts Sample
[C#]
ImportMultiNetSignsFunction.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; using ESRI.ArcGIS.Carto; namespace GPImportSignpostFunctions { /// <summary> /// Summary description for ImportNavStreetsSignsFunction. /// </summary> /// [Guid("267C4FBF-E89A-49b7-9727-FFFB5594733E")] [ClassInterface(ClassInterfaceType.None)] [ProgId("GPImportSignpostFunctions.ImportMultiNetSignsFunction")] class ImportMultiNetSignsFunction : IGPFunction { #region Constants // parameter index constants private const int InputSITable = 0; private const int InputSPTable = 1; private const int ReferenceLineFeatures = 2; private const int OutFeatureClassName = 3; private const int OutStreetsTableName = 4; // field names and types private static readonly string[] SIFieldNames = new string[] { "ID", "SEQNR", "DESTSEQ", "INFOTYP", "RNPART", "TXTCONT", "TXTCONTLC", "CONTYP", "AMBIG" }; private static readonly esriFieldType[] SIFieldTypes = new esriFieldType[] { esriFieldType.esriFieldTypeDouble, esriFieldType.esriFieldTypeInteger, esriFieldType.esriFieldTypeInteger, esriFieldType.esriFieldTypeString, esriFieldType.esriFieldTypeSmallInteger, esriFieldType.esriFieldTypeString, esriFieldType.esriFieldTypeString, esriFieldType.esriFieldTypeSmallInteger, esriFieldType.esriFieldTypeSmallInteger}; private static readonly string[] SPFieldNames = new string[] { "ID", "SEQNR", "TRPELID", "TRPELTYP" }; private static readonly esriFieldType[] SPFieldTypes = new esriFieldType[] { esriFieldType.esriFieldTypeDouble, esriFieldType.esriFieldTypeInteger, esriFieldType.esriFieldTypeDouble, esriFieldType.esriFieldTypeSmallInteger}; private static readonly string LinesIDFieldName = "ID"; private static readonly esriFieldType LinesIDFieldType = esriFieldType.esriFieldTypeDouble; #endregion private IArray m_parameters; public ImportMultiNetSignsFunction() { } #region IGPFunction Members public IArray ParameterInfo { // create and return the parameters for this function: // 1 - Input Sign Information Table // 2 - Input Sign Path Table // 3 - Input Street Features // 4 - Output Signpost Feature Class Name // 5 - Output Signpost Streets Table Name get { IArray paramArray = new ArrayClass(); // 1 - input_sign_information_table IGPParameterEdit paramEdit = new GPParameterClass(); paramEdit.DataType = new DETableTypeClass() as IGPDataType; paramEdit.Value = new DETableClass() as IGPValue; paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput; paramEdit.DisplayName = "Input Sign Information Table"; paramEdit.Enabled = true; paramEdit.Name = "input_sign_information_table"; paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired; paramArray.Add(paramEdit as object); // 2 - input_sign_path_table paramEdit = new GPParameterClass(); paramEdit.DataType = new DETableTypeClass() as IGPDataType; paramEdit.Value = new DETableClass() as IGPValue; paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput; paramEdit.DisplayName = "Input Sign Path Table"; paramEdit.Enabled = true; paramEdit.Name = "input_sign_path_table"; paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired; paramArray.Add(paramEdit as object); // 3 - 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); // 4 - 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); // 5 - 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 SI table has the expected fields IGPParameter gpParam = paramvalues.get_Element(InputSITable) 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, SIFieldNames, SIFieldTypes, validateMessages.GetMessage(InputSITable)); } // Verify chosen input SP table has the expected fields gpParam = paramvalues.get_Element(InputSPTable) as IGPParameter; tableValue = gpUtils.UnpackGPValue(gpParam); // CheckForTableFields will report errors by modifying the relevant GPMessage if (!tableValue.IsEmpty()) { IDETable inputTable = gpUtils.DecodeDETable(tableValue); CheckForTableFields(inputTable, SPFieldNames, SPFieldTypes, validateMessages.GetMessage(InputSPTable)); } // 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 inputSITable; ITable inputSPTable; IFeatureClass inputLineFeatures; IGPParameter gpParam = paramvalues.get_Element(InputSITable) as IGPParameter; IGPValue inputSITableValue = gpUtils.UnpackGPValue(gpParam); IDataset dataset = gpUtils.OpenDataset(inputSITableValue); if (dataset != null) inputSITable = dataset as ITable; else { messages.AddError(1, "Could not open input sign information table."); return; } gpParam = paramvalues.get_Element(InputSPTable) as IGPParameter; IGPValue inputSPTableValue = gpUtils.UnpackGPValue(gpParam); dataset = gpUtils.OpenDataset(inputSPTableValue); if (dataset != null) inputSPTable = dataset as ITable; else { messages.AddError(1, "Could not open input sign path 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(inputSITable, inputSPTable, inputLineFeatures, outputSignsFeatureClass, outputSignDetailTable, messages, trackcancel); #endregion } catch (COMException e) { messages.AddError(1, e.Message); } } public string DisplayName { get { return "Import MultiNet Signs"; } } public string MetadataFile { get { return "ImportMultiNetSignsHelp.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 "ImportMultiNetSigns"; } } 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, string[] fieldNames, esriFieldType[] fieldTypes, 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 inputSignInformationTable, ITable inputSignPathTable, IFeatureClass inputLineFeatures, IFeatureClass outputSignFeatures, ITable outputSignDetailTable, IGPMessages messages, ITrackCancel trackcancel) { #region Find fields //(Validate checked that these exist) IFields inputSITableFields = inputSignInformationTable.Fields; int inSiIdFI = inputSITableFields.FindField("ID"); int inSiInfoTypFI = inputSITableFields.FindField("INFOTYP"); int inSiTxtContFI = inputSITableFields.FindField("TXTCONT"); int inSiConTypFI = inputSITableFields.FindField("CONTYP"); IFields inputSPTableFields = inputSignPathTable.Fields; int inSpIdFI = inputSPTableFields.FindField("ID"); int inSpSeqNrFI = inputSPTableFields.FindField("SEQNR"); int inSpTrpElIdFI = inputSPTableFields.FindField("TRPELID"); int inSpTrpElTypFI = inputSPTableFields.FindField("TRPELTYP"); // 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(inputSignPathTable, inSpTrpElIdFI, -1, 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 cursors for the sign tables we are importing ITableSort spTableSort = new TableSortClass(); spTableSort.Fields = "ID, SEQNR"; spTableSort.set_Ascending("ID", true); spTableSort.set_Ascending("SEQNR", true); spTableSort.QueryFilter = null; spTableSort.Table = inputSignPathTable; spTableSort.Sort(null); ICursor spInputCursor = spTableSort.Rows; ITableSort siTableSort = new TableSortClass(); siTableSort.Fields = "ID, SEQNR, DESTSEQ, RNPART"; siTableSort.set_Ascending("ID", true); siTableSort.set_Ascending("SEQNR", true); siTableSort.set_Ascending("DESTSEQ", true); siTableSort.set_Ascending("RNPART", true); siTableSort.QueryFilter = null; siTableSort.Table = inputSignInformationTable; siTableSort.Sort(null); ICursor siInputCursor = siTableSort.Rows; IRow inputSpTableRow; IRow inputSiTableRow; long currentID = -1, loopSpID, loopSiID; int numOutput = 0; int numInput = 0; bool fetchFeatureDataSucceeded; ArrayList idVals = new System.Collections.ArrayList(2); ArrayList edgesData = new System.Collections.ArrayList(2); ArrayList reverseEdge = new System.Collections.ArrayList(2); SignpostUtilities.FeatureData currentFeatureData = new SignpostUtilities.FeatureData(-1, null); ICurve earlierEdgeCurve, laterEdgeCurve; IPoint earlierEdgeStart, earlierEdgeEnd; IPoint laterEdgeStart, laterEdgeEnd; int nextBranchNum = -1, nextTowardNum = -1; string infoTypText, txtContText; string directionalAsText = ""; string towardText; int conTypVal; int refLinesFCID = inputLineFeatures.ObjectClassID; IGeometry outputSignGeometry; object newOID; inputSpTableRow = spInputCursor.NextRow(); inputSiTableRow = siInputCursor.NextRow(); while (inputSpTableRow != null && inputSiTableRow != null) { currentID = Convert.ToInt64(inputSpTableRow.get_Value(inSpIdFI)); // fetch the edge ID values from the SP table for the current sign ID idVals.Clear(); while (true) { idVals.Add(Convert.ToInt64(inputSpTableRow.get_Value(inSpTrpElIdFI))); inputSpTableRow = spInputCursor.NextRow(); if (inputSpTableRow == null) break; // we've reached the end of the SP table loopSpID = Convert.ToInt64(inputSpTableRow.get_Value(inSpIdFI)); if (loopSpID != currentID) break; // we're now on a new ID value } numInput++; // fetch the FeatureData for each of these edges edgesData.Clear(); fetchFeatureDataSucceeded = true; foreach (long currentIDVal in idVals) { try { currentFeatureData = (SignpostUtilities.FeatureData)lineFeaturesList[currentIDVal]; edgesData.Add(currentFeatureData); } catch { fetchFeatureDataSucceeded = false; if (numInput - numOutput < 100) { messages.AddWarning("Line feature not found for sign with ID: " + Convert.ToString(currentIDVal)); } break; } } if (!fetchFeatureDataSucceeded) continue; // determine the orientation for each of these edges reverseEdge.Clear(); for (int i = 1; i < edgesData.Count; i++) { // get the endpoints of the earlier curve currentFeatureData = (SignpostUtilities.FeatureData)edgesData[i - 1]; earlierEdgeCurve = currentFeatureData.feature as ICurve; earlierEdgeStart = earlierEdgeCurve.FromPoint; earlierEdgeEnd = earlierEdgeCurve.ToPoint; // get the endpoints of the later curve currentFeatureData = (SignpostUtilities.FeatureData)edgesData[i]; laterEdgeCurve = currentFeatureData.feature as ICurve; laterEdgeStart = laterEdgeCurve.FromPoint; laterEdgeEnd = laterEdgeCurve.ToPoint; // determine the orientation of the first edge // (first edge is reversed if its Start point is coincident with either point of the second edge) if (i == 1) reverseEdge.Add(EqualPoints(earlierEdgeStart, laterEdgeStart) || EqualPoints(earlierEdgeStart, laterEdgeEnd)); // determine the orientation of the i'th edge // (i'th edge is reversed if its End point is coincident with either point of the previous edge) reverseEdge.Add(EqualPoints(laterEdgeEnd, earlierEdgeStart) || EqualPoints(laterEdgeEnd, earlierEdgeEnd)); } // write out the sign geometry to the featureBuffer outputSignGeometry = MakeSignGeometry(edgesData, reverseEdge); featureBuffer.Shape = outputSignGeometry; // fetch the signpost information from the SI table for the current sign ID nextBranchNum = 0; nextTowardNum = 0; featureBuffer.set_Value(outExitNameFI, ""); while (inputSiTableRow != null) { loopSiID = Convert.ToInt64(inputSiTableRow.get_Value(inSiIdFI)); if (loopSiID < currentID) { inputSiTableRow = siInputCursor.NextRow(); continue; } else if (loopSiID > currentID) { break; // we're now on a new ID value } infoTypText = inputSiTableRow.get_Value(inSiInfoTypFI) as string; txtContText = inputSiTableRow.get_Value(inSiTxtContFI) as string; conTypVal = Convert.ToInt32(inputSiTableRow.get_Value(inSiConTypFI)); switch (infoTypText) { case "4E": // exit number featureBuffer.set_Value(outExitNameFI, txtContText); break; case "9D": // place name case "4I": // other destination // check for schema overflow if (nextTowardNum > SignpostUtilities.MaxBranchCount - 1) { inputSiTableRow = siInputCursor.NextRow(); continue; } // set values featureBuffer.set_Value(outTowardXFI[nextTowardNum], txtContText); featureBuffer.set_Value(outTowardXLngFI[nextTowardNum], "en"); // get ready for next toward nextTowardNum++; break; case "6T": // street name case "RN": // route number if (conTypVal == 2) // toward { // check for schema overflow if (nextTowardNum > SignpostUtilities.MaxBranchCount - 1) { inputSiTableRow = siInputCursor.NextRow(); continue; } // set values featureBuffer.set_Value(outTowardXFI[nextTowardNum], txtContText); featureBuffer.set_Value(outTowardXLngFI[nextTowardNum], "en"); // get ready for next toward nextTowardNum++; } else // branch { // check for schema overflow if (nextBranchNum > SignpostUtilities.MaxBranchCount - 1) { inputSiTableRow = siInputCursor.NextRow(); continue; } // set values featureBuffer.set_Value(outBranchXFI[nextBranchNum], txtContText); featureBuffer.set_Value(outBranchXDirFI[nextBranchNum], ""); featureBuffer.set_Value(outBranchXLngFI[nextBranchNum], "en"); // get ready for next branch nextBranchNum++; } break; case "7G": // route directional // convert the directional value to a string switch (txtContText) { case "1": directionalAsText = "N"; break; case "2": directionalAsText = "E"; break; case "3": directionalAsText = "S"; break; case "4": directionalAsText = "W"; break; } if (conTypVal == 2) // toward { // check for schema underflow if (nextTowardNum == 0) { inputSiTableRow = siInputCursor.NextRow(); continue; } // append directional text to the previous toward text value towardText = featureBuffer.get_Value(outTowardXFI[nextTowardNum - 1]) as string; towardText = towardText + " " + directionalAsText; featureBuffer.set_Value(outTowardXFI[nextTowardNum - 1], towardText); } else // branch { // check for schema underflow if (nextBranchNum == 0) { inputSiTableRow = siInputCursor.NextRow(); continue; } // set value of the Dir field on the previous branch featureBuffer.set_Value(outBranchXDirFI[nextBranchNum - 1], directionalAsText); } break; } // switch inputSiTableRow = siInputCursor.NextRow(); } // each SI table record // 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(outTblEdgeFCIDFI, refLinesFCID); for (int i = 0; i < edgesData.Count; i++) { currentFeatureData = (SignpostUtilities.FeatureData)edgesData[i]; tableRowBuffer.set_Value(outTblSequenceFI, i + 1); tableRowBuffer.set_Value(outTblEdgeFIDFI, currentFeatureData.OID); if ((bool)reverseEdge[i]) { tableRowBuffer.set_Value(outTblEdgeFrmPosFI, 1.0); tableRowBuffer.set_Value(outTblEdgeToPosFI, 0.0); } else { tableRowBuffer.set_Value(outTblEdgeFrmPosFI, 0.0); tableRowBuffer.set_Value(outTblEdgeToPosFI, 1.0); } // insert detail record tableInsertCursor.InsertRow(tableRowBuffer); } numOutput++; if ((numOutput % 100) == 0) { // check for user cancel if (!trackcancel.Continue()) throw (new COMException("Function cancelled.")); } } // outer while // 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(ArrayList edgesData, ArrayList reverseEdge) { ISegmentCollection resultSegments = new PolylineClass(); SignpostUtilities.FeatureData currentFeatureData = new SignpostUtilities.FeatureData(-1, null); ICurve currentCurve, resultCurve; for (int i = 0; i < edgesData.Count; i++) { // fetch the curve and reverse it as needed currentFeatureData = (SignpostUtilities.FeatureData)edgesData[i]; currentCurve = currentFeatureData.feature as ICurve; if ((bool)reverseEdge[i]) currentCurve.ReverseOrientation(); // trim the first and last geometries so that they only cover 25% of the street feature if (i == 0) currentCurve.GetSubcurve(0.75, 1.0, true, out resultCurve); else if (i == (edgesData.Count - 1)) currentCurve.GetSubcurve(0.0, 0.25, true, out resultCurve); else resultCurve = currentCurve; // add the resulting geometry to the collection resultSegments.AddSegmentCollection(resultCurve as ISegmentCollection); } return resultSegments as IGeometry; } } }
[Visual Basic .NET]
ImportMultiNetSignsFunction.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 Imports ESRI.ArcGIS.Carto Namespace GPImportSignpostFunctions <Guid("F5AB9C9C-8193-40fa-99B2-369545BCAAEE")> _ <ClassInterface(ClassInterfaceType.None)> _ <ProgId("GPImportSignpostFunctions.ImportMultiNetSignsFunction")> _ Public Class ImportMultiNetSignsFunction Implements IGPFunction #Region "Constants" ' parameter index constants Private Const InputSITable As Integer = 0 Private Const InputSPTable As Integer = 1 Private Const InputTable As Integer = 0 Private Const ReferenceLineFeatures As Integer = 2 Private Const OutFeatureClassName As Integer = 3 Private Const OutStreetsTableName As Integer = 4 ' field names and types Private Shared ReadOnly SIFieldNames() As String = New String() _ {"ID", "SEQNR", "DESTSEQ", "INFOTYP", "RNPART", "TXTCONT", "TXTCONTLC", "CONTYP", "AMBIG"} Private Shared ReadOnly SIFieldTypes() As esriFieldType = New esriFieldType() _ {esriFieldType.esriFieldTypeDouble, _ esriFieldType.esriFieldTypeInteger, _ esriFieldType.esriFieldTypeInteger, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeSmallInteger, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeString, _ esriFieldType.esriFieldTypeSmallInteger, _ esriFieldType.esriFieldTypeSmallInteger} Private Shared ReadOnly SPFieldNames() As String = New String() _ {"ID", "SEQNR", "TRPELID", "TRPELTYP"} Private Shared ReadOnly SPFieldTypes() As esriFieldType = New esriFieldType() _ {esriFieldType.esriFieldTypeDouble, _ esriFieldType.esriFieldTypeInteger, _ esriFieldType.esriFieldTypeDouble, _ esriFieldType.esriFieldTypeSmallInteger} Private Shared ReadOnly LinesIDFieldName As String = "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_sign_information_table Dim paramEdit As IGPParameterEdit = New GPParameter paramEdit.DataType = New DETableType paramEdit.Value = New DETable paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput paramEdit.DisplayName = "Input Sign Information Table" paramEdit.Enabled = True paramEdit.Name = "input_sign_information_table" paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired gpParamArray.Add(paramEdit) ' 2 - input_sign_path_table paramEdit = New GPParameter paramEdit.DataType = New DETableType paramEdit.Value = New DETable paramEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionInput paramEdit.DisplayName = "Input Sign Path Table" paramEdit.Enabled = True paramEdit.Name = "input_sign_path_table" paramEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired gpParamArray.Add(paramEdit) ' 3 - 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) ' 4 - 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) ' 5 - 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 SI table has the expected fields Dim gpParam As IGPParameter = CType(paramvalues.Element(InputSITable), 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, SIFieldNames, SIFieldTypes, validateMessages.GetMessage(InputSITable)) End If ' Verify chosen input SP table has the expected fields gpParam = CType(paramvalues.Element(InputSPTable), IGPParameter) tableValue = 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, SPFieldNames, SPFieldTypes, validateMessages.GetMessage(InputSPTable)) 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 inputSITableAsITable As ITable Dim inputSPTableAsITable As ITable Dim inputLineFeatures As IFeatureClass Dim gpParam As IGPParameter = CType(paramvalues.Element(InputSITable), IGPParameter) Dim inputSITableValue As IGPValue = gpUtils.UnpackGPValue(gpParam) Dim dataset As IDataset = gpUtils.OpenDataset(inputSITableValue) If dataset IsNot Nothing Then inputSITableAsITable = CType(dataset, ITable) Else messages.AddError(1, "Could not open input sign information table.") Return End If gpParam = CType(paramvalues.Element(InputSPTable), IGPParameter) Dim inputSPTableValue As IGPValue = gpUtils.UnpackGPValue(gpParam) dataset = gpUtils.OpenDataset(inputSPTableValue) If dataset IsNot Nothing Then inputSPTableAsITable = CType(dataset, ITable) Else messages.AddError(1, "Could not open input sign path 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(inputSITableAsITable, inputSPTableAsITable, 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 MultiNet Signs" End Get End Property Public ReadOnly Property MetadataFile() As String Implements IGPFunction.MetadataFile Get Return "ImportMultiNetSignsHelp.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 "ImportMultiNetSigns" 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 fieldNames As String(), ByVal fieldTypes As esriFieldType(), _ 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 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 inputSignInformationTable As ITable, ByVal inputSignPathTable 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 inputSITableFields As IFields = inputSignInformationTable.Fields Dim inSiIdFI As Integer = inputSITableFields.FindField("ID") Dim inSiInfoTypFI As Integer = inputSITableFields.FindField("INFOTYP") Dim inSiTxtContFI As Integer = inputSITableFields.FindField("TXTCONT") Dim inSiConTypFI As Integer = inputSITableFields.FindField("CONTYP") Dim inputSPTableFields As IFields = inputSignPathTable.Fields Dim inSpIdFI As Integer = inputSPTableFields.FindField("ID") Dim inSpSeqNrFI As Integer = inputSPTableFields.FindField("SEQNR") Dim inSpTrpElIdFI As Integer = inputSPTableFields.FindField("TRPELID") Dim inSpTrpElTypFI As Integer = inputSPTableFields.FindField("TRPELTYP") ' 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 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(inputSignPathTable, inSpTrpElIdFI, -1, 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 cursors for the sign tables we are importing Dim spTableSort As ITableSort = New TableSort() spTableSort.Fields = "ID, SEQNR" spTableSort.Ascending("ID") = True spTableSort.Ascending("SEQNR") = True spTableSort.QueryFilter = Nothing spTableSort.Table = inputSignPathTable spTableSort.Sort(Nothing) Dim spInputCursor As ICursor = spTableSort.Rows Dim siTableSort As ITableSort = New TableSort() siTableSort.Fields = "ID, SEQNR, DESTSEQ, RNPART" siTableSort.Ascending("ID") = True siTableSort.Ascending("SEQNR") = True siTableSort.Ascending("DESTSEQ") = True siTableSort.Ascending("RNPART") = True siTableSort.QueryFilter = Nothing siTableSort.Table = inputSignInformationTable siTableSort.Sort(Nothing) Dim siInputCursor As ICursor = siTableSort.Rows Dim inputSpTableRow As IRow Dim inputSiTableRow As IRow Dim currentID As Long = -1, loopSpID As Long, loopSiID As Long Dim numOutput As Integer = 0 Dim numInput As Integer = 0 Dim fetchFeatureDataSucceeded As Boolean Dim idVals As ArrayList = New System.Collections.ArrayList(2) Dim edgesData As ArrayList = New System.Collections.ArrayList(2) Dim reverseEdge As ArrayList = New System.Collections.ArrayList(2) Dim currentFeatureData As SignpostUtilities.FeatureData = New SignpostUtilities.FeatureData(-1, Nothing) Dim earlierEdgeCurve As ICurve, laterEdgeCurve As ICurve Dim earlierEdgeStart As IPoint, earlierEdgeEnd As IPoint Dim laterEdgeStart As IPoint, laterEdgeEnd As IPoint Dim nextBranchNum As Integer = -1, nextTowardNum As Integer = -1 Dim infoTypText As String, txtContText As String Dim directionalAsText As String = "" Dim towardText As String Dim conTypVal As Integer Dim refLinesFCID As Integer = inputLineFeatures.ObjectClassID Dim outputSignGeometry As IGeometry Dim newOID As Object inputSpTableRow = spInputCursor.NextRow() inputSiTableRow = siInputCursor.NextRow() While inputSpTableRow IsNot Nothing And inputSiTableRow IsNot Nothing currentID = Convert.ToInt64(inputSpTableRow.Value(inSpIdFI)) ' fetch the edge ID values from the SP table for the current sign ID idVals.Clear() While (True) idVals.Add(Convert.ToInt64(inputSpTableRow.Value(inSpTrpElIdFI))) inputSpTableRow = spInputCursor.NextRow() If (inputSpTableRow Is Nothing) Then Exit While ' we've reached the end of the SP table End If loopSpID = Convert.ToInt64(inputSpTableRow.Value(inSpIdFI)) If (loopSpID <> currentID) Then Exit While ' we're now on a new ID value End If End While numInput += 1 ' fetch the FeatureData for each of these edges edgesData.Clear() fetchFeatureDataSucceeded = True For Each currentIDVal As Long In idVals Try currentFeatureData = CType(lineFeaturesList(currentIDVal), SignpostUtilities.FeatureData) edgesData.Add(currentFeatureData) Catch fetchFeatureDataSucceeded = False If (numInput - numOutput < 100) Then messages.AddWarning("Line feature not found for sign with ID: " + _ Convert.ToString(currentIDVal)) End If Exit For End Try Next If Not fetchFeatureDataSucceeded Then Continue While ' determine the orientation for each of these edges reverseEdge.Clear() For i As Integer = 1 To edgesData.Count - 1 ' get the endpoints of the earlier curve currentFeatureData = CType(edgesData(i - 1), SignpostUtilities.FeatureData) earlierEdgeCurve = CType(currentFeatureData.feature, ICurve) earlierEdgeStart = earlierEdgeCurve.FromPoint earlierEdgeEnd = earlierEdgeCurve.ToPoint ' get the endpoints of the later curve currentFeatureData = CType(edgesData(i), SignpostUtilities.FeatureData) laterEdgeCurve = CType(currentFeatureData.feature, ICurve) laterEdgeStart = laterEdgeCurve.FromPoint laterEdgeEnd = laterEdgeCurve.ToPoint ' determine the orientation of the first edge ' (first edge is reversed if its Start point is coincident with either point of the second edge) If (i = 1) Then reverseEdge.Add(EqualPoints(earlierEdgeStart, laterEdgeStart) Or EqualPoints(earlierEdgeStart, laterEdgeEnd)) End If ' determine the orientation of the i'th edge ' (i'th edge is reversed if its End point is coincident with either point of the previous edge) reverseEdge.Add(EqualPoints(laterEdgeEnd, earlierEdgeStart) Or EqualPoints(laterEdgeEnd, earlierEdgeEnd)) Next i ' write out the sign geometry to the featureBuffer outputSignGeometry = MakeSignGeometry(edgesData, reverseEdge) featureBuffer.Shape = outputSignGeometry ' fetch the signpost information from the SI table for the current sign ID nextBranchNum = 0 nextTowardNum = 0 featureBuffer.Value(outExitNameFI) = "" While inputSiTableRow IsNot Nothing loopSiID = Convert.ToInt64(inputSiTableRow.Value(inSiIdFI)) If (loopSiID < currentID) Then inputSiTableRow = siInputCursor.NextRow() Continue While ElseIf (loopSiID > currentID) Then Exit While ' we're now on a new ID value End If infoTypText = CType(inputSiTableRow.Value(inSiInfoTypFI), String) txtContText = CType(inputSiTableRow.Value(inSiTxtContFI), String) conTypVal = Convert.ToInt32(inputSiTableRow.Value(inSiConTypFI)) Select Case infoTypText Case "4E" ' exit number featureBuffer.Value(outExitNameFI) = txtContText Case "9D", "4I" ' place name or other destination ' check for schema overflow If (nextTowardNum > SignpostUtilities.MaxBranchCount - 1) Then inputSiTableRow = siInputCursor.NextRow() Continue While End If ' set values featureBuffer.Value(outTowardXFI(nextTowardNum)) = txtContText featureBuffer.Value(outTowardXLngFI(nextTowardNum)) = "en" ' get ready for next toward nextTowardNum += 1 Case "6T", "RN" ' street name or route number If (conTypVal = 2) Then ' toward ' check for schema overflow If (nextTowardNum > SignpostUtilities.MaxBranchCount - 1) Then inputSiTableRow = siInputCursor.NextRow() Continue While End If ' set values featureBuffer.Value(outTowardXFI(nextTowardNum)) = txtContText featureBuffer.Value(outTowardXLngFI(nextTowardNum)) = "en" ' get ready for next toward nextTowardNum += 1 Else ' branch ' check for schema overflow If (nextBranchNum > SignpostUtilities.MaxBranchCount - 1) Then inputSiTableRow = siInputCursor.NextRow() Continue While End If ' set values featureBuffer.Value(outBranchXFI(nextBranchNum)) = txtContText featureBuffer.Value(outBranchXDirFI(nextBranchNum)) = "" featureBuffer.Value(outBranchXLngFI(nextBranchNum)) = "en" ' get ready for next branch nextBranchNum += 1 End If Case "7G" ' route directional ' convert the directional value to a string Select Case txtContText Case "1" directionalAsText = "N" Case "2" directionalAsText = "E" Case "3" directionalAsText = "S" Case "4" directionalAsText = "W" End Select If (conTypVal = 2) Then ' toward ' check for schema underflow If (nextTowardNum = 0) Then inputSiTableRow = siInputCursor.NextRow() Continue While End If ' append directional text to the previous toward text value towardText = CType(featureBuffer.Value(outTowardXFI(nextTowardNum - 1)), String) towardText = towardText + " " + directionalAsText featureBuffer.Value(outTowardXFI(nextTowardNum - 1)) = towardText Else ' branch ' check for schema underflow If (nextBranchNum = 0) Then inputSiTableRow = siInputCursor.NextRow() Continue While End If ' set value of the Dir field on the previous branch featureBuffer.Value(outBranchXDirFI(nextBranchNum - 1)) = directionalAsText End If End Select inputSiTableRow = siInputCursor.NextRow() End While ' each SI table record ' 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(outTblEdgeFCIDFI) = refLinesFCID For i As Integer = 0 To edgesData.Count - 1 currentFeatureData = CType(edgesData(i), SignpostUtilities.FeatureData) tableRowBuffer.Value(outTblSequenceFI) = i + 1 tableRowBuffer.Value(outTblEdgeFIDFI) = currentFeatureData.OID If (CType(reverseEdge(i), Boolean)) Then tableRowBuffer.Value(outTblEdgeFrmPosFI) = 1.0 tableRowBuffer.Value(outTblEdgeToPosFI) = 0.0 Else tableRowBuffer.Value(outTblEdgeFrmPosFI) = 0.0 tableRowBuffer.Value(outTblEdgeToPosFI) = 1.0 End If ' insert detail record tableInsertCursor.InsertRow(tableRowBuffer) Next i 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 While ' outer while ' 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 edgesData As ArrayList, ByVal reverseEdge As ArrayList) As IGeometry Dim resultSegments As ISegmentCollection = New Polyline() Dim currentFeatureData As SignpostUtilities.FeatureData = New SignpostUtilities.FeatureData(-1, Nothing) Dim currentCurve As ICurve, resultCurve As ICurve For i As Integer = 0 To edgesData.Count - 1 ' fetch the curve and reverse it as needed currentFeatureData = CType(edgesData(i), SignpostUtilities.FeatureData) currentCurve = CType(currentFeatureData.feature, ICurve) If (CType(reverseEdge(i), Boolean)) Then currentCurve.ReverseOrientation() End If ' trim the first and last geometries so that they only cover 25% of the street feature If (i = 0) Then currentCurve.GetSubcurve(0.75, 1.0, True, resultCurve) ElseIf (i = (edgesData.Count - 1)) Then currentCurve.GetSubcurve(0.0, 0.25, True, resultCurve) Else resultCurve = currentCurve End If ' add the resulting geometry to the collection resultSegments.AddSegmentCollection(CType(resultCurve, ISegmentCollection)) Next i Return CType(resultSegments, IGeometry) End Function End Class End Namespace