About the ArcGIS Network Analyst extension barrier location editor Sample
[C#]
EditorForm.cs
using System; using System.Windows.Forms; using ESRI.ArcGIS.ArcMapUI; using ESRI.ArcGIS.Display; using ESRI.ArcGIS.Framework; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.NetworkAnalyst; namespace NABarrierLocationEditor { public partial class EditorForm : Form { #region Member Variables private readonly static string EDGE_ALONG = "Along Digitized"; private readonly static string EDGE_AGAINST = "Against Digitized"; IApplication m_app; INAContext m_context; IFeature m_barrier; #endregion #region Initialization public EditorForm(IApplication app, INAContext context, IFeature barrier) { m_barrier = barrier; m_app = app; m_context = context; InitializeComponent(); LoadDatagrids(); } /// <summary> /// Load both the Edge and Junction dataGrids with the information in the barrier feature /// <param name="barrier">The barrier being loaded into dataGrids</param> /// </summary> void LoadDatagrids() { // Populate the cell with the direction drop down ((DataGridViewComboBoxColumn)dataGridViewEdges.Columns[1]).Items.AddRange(EDGE_ALONG, EDGE_AGAINST); ; // get the location ranges out of the barrier feature var naLocRangesObject = m_barrier as INALocationRangesObject; var naLocRanges = naLocRangesObject.NALocationRanges; if (naLocRanges == null) throw new Exception("Selected barrier has a null NALocationRanges value"); // add all of the junctions included in the barrier to the Junctions dataGrid long junctionCount = naLocRanges.JunctionCount; int junctionEID = -1; for (int i = 0; i < junctionCount; i++) { naLocRanges.QueryJunction(i, ref junctionEID); int rowIndex = dataGridViewJunctions.Rows.Add(); dataGridViewJunctions.Rows[rowIndex].SetValues(junctionEID); } // add all of the edges included in the barrier to the Edges dataGrid long edgeRangeCount = naLocRanges.EdgeRangeCount; int edgeEID = -1; double fromPosition, toPosition; fromPosition = toPosition = -1; esriNetworkEdgeDirection edgeDirection = esriNetworkEdgeDirection.esriNEDNone; for (int i = 0; i < edgeRangeCount; i++) { naLocRanges.QueryEdgeRange(i, ref edgeEID, ref edgeDirection, ref fromPosition, ref toPosition); string directionValue = ""; if (edgeDirection == esriNetworkEdgeDirection.esriNEDAlongDigitized) directionValue = EDGE_ALONG; else if (edgeDirection == esriNetworkEdgeDirection.esriNEDAgainstDigitized) directionValue = EDGE_AGAINST; dataGridViewEdges.Rows.Add(edgeEID, directionValue, fromPosition, toPosition); } } #endregion #region Button Clicks /// <summary> /// Occurs when the user clicks the Cancel button. /// <param name="sender">The control raising this event</param> /// <param name="e">Arguments associated with the event</param> /// </summary> private void btnCancel_Click(object sender, EventArgs e) { this.Close(); } /// <summary> /// Occurs when the user clicks the Save button. /// The barrier information is collected out of the junction and edge barrier /// dataGrids, then stored back into the original barrier feature as a replacement /// to the existing barrier information. The original geometry of the barrier remains /// unaltered. /// <param name="sender">The control raising this event</param> /// <param name="e">Arguments associated with the event</param> /// </summary> private void btnSave_Click(object sender, EventArgs e) { if (!ValidateDataGrid(dataGridViewEdges)) return; if (!ValidateDataGrid(dataGridViewJunctions)) return; // The existing NALocationRanges for the barrier will be replaced with a new one INALocationRanges naLocRanges = new NALocationRangesClass(); // First gather the edge ranges foreach (DataGridViewRow row in dataGridViewEdges.Rows) { // ignore the extra row in the dataGrid if (row.IsNewRow) continue; // gather the EID value for the new range int eid = Int32.Parse(row.Cells[0].Value.ToString()); // gather the edge direction value for the new range string directionValue = row.Cells[1].Value.ToString(); esriNetworkEdgeDirection direction = esriNetworkEdgeDirection.esriNEDNone; if (directionValue == EDGE_ALONG) direction = esriNetworkEdgeDirection.esriNEDAlongDigitized; else if (directionValue == EDGE_AGAINST) direction = esriNetworkEdgeDirection.esriNEDAgainstDigitized; // gather the from and to position values for the new range double fromPos = Double.Parse(row.Cells[2].Value.ToString()); double toPos = Double.Parse(row.Cells[3].Value.ToString()); // load the values for this range into the NALocationRanges object naLocRanges.AddEdgeRange(eid, direction, fromPos, toPos); } // Now gather the junctions to be included in the barrier foreach (DataGridViewRow row in dataGridViewJunctions.Rows) { // ignore the extra row in the dataGrid if (row.IsNewRow) continue; // gather the EID value for the junction to include int eid = Int32.Parse(row.Cells[0].Value.ToString()); // load this junction into the NALocationRanges object naLocRanges.AddJunction(eid); } // Cast the barrier feature to INALocationRanges Object, then populate // its NALocationRanges value with the new barrier that was created above. // Then, save the new barrier with a call to Store() INALocationRangesObject naLocationRangesObject = m_barrier as INALocationRangesObject; naLocationRangesObject.NALocationRanges = naLocRanges; m_barrier.Store(); this.Close(); } /// <summary> /// Occurs when the user clicks the Zoom To Barrier Geometry button. /// The map will zoom to the extent of the Shape of the barrier. /// <param name="sender">The control raising this event</param> /// <param name="e">Arguments associated with the event</param> /// </summary> private void btnZoomToBarrier_Click(object sender, EventArgs e) { IMxDocument mxDoc = m_app.Document as IMxDocument; mxDoc.ActiveView.Extent = m_barrier.Extent; mxDoc.ActiveView.Refresh(); } #endregion #region Validation /// <summary> /// ValidateDataGrid goes row by row and checks that the values in the grid /// are valid /// <param name="dgv">The dataGrid to validate</param> /// </summary> private bool ValidateDataGrid(DataGridView dgv) { // we do all of our validation when the save button is clicked foreach (DataGridViewRow row in dgv.Rows) { ValidateRow(row); if (row.ErrorText != "") { dgv.FirstDisplayedScrollingRowIndex = row.Index; System.Windows.Forms.MessageBox.Show("You cannot save until all row errors are cleared.", "Barrier Location Editor Warning"); return false; } } return true; } /// <summary> /// ValidateRow checks the cell value in the passed-in row /// <param name="row">The row to validate</param> /// <param name="columns">The fields to be validated</param> /// <param name="datagridviewName">The name of the dataGrid whose row is being validated</param> /// </summary> void ValidateRow(DataGridViewRow row) { // the extra row to add values does not need to be validated if (row.IsNewRow) return; row.ErrorText = ""; // validate each column foreach (DataGridViewColumn column in row.DataGridView.Columns) { // none of the column values can be empty if (row.Cells[column.Name].Value == null || row.Cells[column.Name].Value.ToString() == "") { row.ErrorText = "There cannot be any empty cells"; return; } string value = row.Cells[column.Name].Value.ToString(); switch (column.Name) { case "JunctionEID": if (!ValidateEID(value, esriNetworkElementType.esriNETJunction)) row.ErrorText += " Junction EID must correspond to a valid network junction"; break; case "EdgeEID": if (!ValidateEID(value, esriNetworkElementType.esriNETEdge)) row.ErrorText += " Edge EID must correspond to a valid network edge"; break; case "Direction": if (value != "Along Digitized" && value != "Against Digitized") row.ErrorText += " Direction must be Along or Against Digitized"; break; case "fromPos": double fromPos = -1; if (!Double.TryParse(value, out fromPos) || fromPos < 0 || fromPos > 1) row.ErrorText += " FromPosition must be a positive number between zero and one"; break; case "toPos": double toPos = -1; if (!Double.TryParse(value, out toPos) || toPos < 0 || toPos > 1) row.ErrorText += " ToPosition must be a positive number between zero and one"; break; default: throw new Exception("Unexpected Column"); } } // Now, validate that the from position is always less than the two position // FromPosition and ToPosition only matter for edge barriers if (row.DataGridView.Name == "dataGridViewEdges") { if (row.Cells["FromPos"].Value == null || row.Cells["ToPos"].Value == null) { row.ErrorText += " FromPosition and ToPosition must have valid decimal values between 0 and 1"; return; } double fromPos = -1; Double.TryParse(row.Cells["FromPos"].Value.ToString(), out fromPos); double toPos = -1; Double.TryParse(row.Cells["ToPos"].Value.ToString(), out toPos); if (fromPos > toPos) { row.ErrorText += " FromPosition must be equal to or less than the ToPosition"; return; } } } /// <summary> /// Verify that the EID corresponds to a valid network element /// <param name="value">The EID passed as a string</param> /// <param name="elementType">The type of element to be verified</param> /// </summary> bool ValidateEID(string value, esriNetworkElementType elementType) { // validate that the EID is a valid integer int eid = -1; if (!Int32.TryParse(value, out eid) || eid < 1) return false; // QueryEdge and QueryJunction will throw exceptions if the EID doesn't match any elements var netQuery = m_context.NetworkDataset as INetworkQuery; try { switch (elementType) { case esriNetworkElementType.esriNETJunction: var junction = netQuery.CreateNetworkElement(esriNetworkElementType.esriNETJunction) as INetworkJunction; netQuery.QueryJunction(eid, junction); break; case esriNetworkElementType.esriNETEdge: var edge = netQuery.CreateNetworkElement(esriNetworkElementType.esriNETEdge) as INetworkEdge; netQuery.QueryEdge(eid, esriNetworkEdgeDirection.esriNEDAlongDigitized, edge); break; default: return false; } } catch { return false; } return true; } #endregion #region Flash the Geometry /// <summary> /// Occurs when a dataGrid row header is clicked. /// It is used here to flash the geometry of the newly selected row /// <param name="sender">The control raising this event</param> /// <param name="e">Arguments associated with the event</param> /// </summary> private void dataGridView_RowHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) { // make sure none of the cells are in edit mode. When in edit mode, the values obtained // programmatically will not yet match the value the user has changed the cell to DataGridView dgv = (DataGridView)sender; dgv.EndEdit(); // Only flash when there is one selected row if (dgv.SelectedRows.Count > 1) return; DataGridViewRow selectedRow = dgv.SelectedRows[0]; // If it is the extra dataGrid row or has errors, then don't try to flash it ValidateRow(selectedRow); if (selectedRow.IsNewRow || selectedRow.ErrorText != "") return; // also, if any of the row's cell have no value, then don't want to flash it foreach (DataGridViewCell cell in selectedRow.Cells) if (cell.Value == null) return; // use the EID to obtain the barrier's corresponding network element and source feature INetworkElement element = GetElementByEID(selectedRow.Cells[0].Value.ToString(), dgv.Name); if (element == null) return; IFeature sourceFeature = GetSourceFeature(element); // For an edge, get the part geometry of the barrier covered portion of the source feature // that should be displayed INetworkEdge netEdge = element as INetworkEdge; esriNetworkEdgeDirection displayDirection = esriNetworkEdgeDirection.esriNEDNone; if (netEdge != null) { sourceFeature.Shape = GetBarrierSubcurve(netEdge, sourceFeature, selectedRow); displayDirection = GetDirectionValue(selectedRow); } // Draw FlashFeature(sourceFeature, displayDirection); } /// <summary> /// Determine the esriNetworkEdgeDirection from the value in the dataGridView cell /// <param name="selectedRow">The row containing the barrier's location range information</param> /// </summary> private esriNetworkEdgeDirection GetDirectionValue(DataGridViewRow selectedRow) { esriNetworkEdgeDirection direction = esriNetworkEdgeDirection.esriNEDNone; string textValue = selectedRow.Cells[1].Value.ToString(); if (textValue == EDGE_ALONG) direction = esriNetworkEdgeDirection.esriNEDAlongDigitized; else if (textValue == EDGE_AGAINST) direction = esriNetworkEdgeDirection.esriNEDAgainstDigitized; return direction; } /// <summary> /// Take a network edge, a source feature, and a row from the edges dataGrid, and determine /// the geometry to be flashed on the map /// <param name="netEdge">The edge upon which the barrier resides</param> /// <param name="sourceFeature">The source feature corresponding to the network edge</param> /// <param name="selectedRow">The row containing the barrier's location range information</param> /// </summary> private ICurve GetBarrierSubcurve(INetworkEdge netEdge, IFeature sourceFeature, DataGridViewRow selectedRow) { // value for displaying the entire source feature double fromPosition = 0; double toPosition = 1; // Find the values for displaying only the element portion of the source feature double fromElementPosition, toElementPosition; netEdge.QueryPositions(out fromElementPosition, out toElementPosition); // due to the element possibly being in the against digitized direction, // fromPosition could be greater than toPosition. If that is the case, swap the values if (fromElementPosition > toElementPosition) { double tmp = fromElementPosition; fromElementPosition = toElementPosition; toElementPosition = tmp; } esriNetworkEdgeDirection direction = GetDirectionValue(selectedRow); if (direction == esriNetworkEdgeDirection.esriNEDNone) return null; // Flash the edge if (rbFlashElementPortion.Checked) { fromPosition = fromElementPosition; toPosition = toElementPosition; } // Flash the barrier portion of the edge else if (rbFlashBarrierPortion.Checked) { double fromBarrierPosition = -1; double toBarrierPosition = -1; // gather the from and to position values for the barrier fromBarrierPosition = Double.Parse(selectedRow.Cells[2].Value.ToString()); toBarrierPosition = Double.Parse(selectedRow.Cells[3].Value.ToString()); // for barriers in the against direction, we need to adjust that the element position is if (direction == esriNetworkEdgeDirection.esriNEDAgainstDigitized) { fromBarrierPosition = 1 - fromBarrierPosition; toBarrierPosition = 1 - toBarrierPosition; } // use the positioning along the element of the barrier // to get the position along the original source feature fromPosition = fromElementPosition + (fromBarrierPosition * (toElementPosition - fromElementPosition)); toPosition = fromElementPosition + (toBarrierPosition * (toElementPosition - fromElementPosition)); } if (fromPosition > toPosition) { double tmp = fromPosition; fromPosition = toPosition; toPosition = tmp; } // get the subspan on the polyline that represents the from and to positions we specified ICurve displayCurve; ICurve sourceCurve = sourceFeature.Shape as ICurve; sourceCurve.GetSubcurve(fromPosition, toPosition, true, out displayCurve); return displayCurve; } /// <summary> /// Take an EID value as a string from one of the dataGridView controls and find /// the network element that corresponds to the EID /// <param name="eidString">The EID value as a string</param> /// <param name="datagridviewName">The name of the dataGrid that held the EID</param> /// </summary> private INetworkElement GetElementByEID(string eidString, string datagridviewName) { int eid = -1; if (!Int32.TryParse(eidString, out eid)) return null; INetworkQuery netQuery = m_context.NetworkDataset as INetworkQuery; INetworkEdge edge = netQuery.CreateNetworkElement(esriNetworkElementType.esriNETEdge) as INetworkEdge; INetworkJunction junction = netQuery.CreateNetworkElement(esriNetworkElementType.esriNETJunction) as INetworkJunction; INetworkElement element = null; try { // Populate the network element from the EID if (datagridviewName == "dataGridViewEdges") { netQuery.QueryEdge(eid, esriNetworkEdgeDirection.esriNEDAlongDigitized, edge); element = edge as INetworkElement; } else if (datagridviewName == "dataGridViewJunctions") { netQuery.QueryJunction(eid, junction); element = junction as INetworkElement; } } catch { // if the query fails, the element will not be displayed } return element; } /// <summary> /// Take a network element and return its corresponding source feature /// <param name="element">The return source feature corresponds to this element</param> /// </summary> private IFeature GetSourceFeature(INetworkElement element) { // To draw the network element, we will need its corresponding source feature information // Get the sourceID and OID from the element int sourceID = element.SourceID; int sourceOID = element.OID; // Get the source feature from the network source INetworkSource netSource = m_context.NetworkDataset.get_SourceByID(sourceID); IFeatureClassContainer fClassContainer = m_context.NetworkDataset as IFeatureClassContainer; IFeatureClass sourceFClass = fClassContainer.get_ClassByName(netSource.Name); return sourceFClass.GetFeature(sourceOID); } /// <summary> /// Flash the feature geometry on the map /// <param name="pFeature">The feature being flashed</param> /// <param name="pMxDoc">A hook to the application display</param> /// <param name="direction">The digitized direction of the barrier with respect to the underlying source feature</param> /// </summary> private void FlashFeature(IFeature pFeature, esriNetworkEdgeDirection direction) { IMxDocument pMxDoc = m_app.Document as IMxDocument; // Start drawing on screen. pMxDoc.ActiveView.ScreenDisplay.StartDrawing(0, (short)esriScreenCache.esriNoScreenCache); // Switch functions based on Geometry type. switch (pFeature.Shape.GeometryType) { case esriGeometryType.esriGeometryPolyline: FlashLine(pMxDoc.ActiveView.ScreenDisplay, pFeature.Shape, direction); break; case esriGeometryType.esriGeometryPolygon: // no network elements can be polygons break; case esriGeometryType.esriGeometryPoint: FlashPoint(pMxDoc.ActiveView.ScreenDisplay, pFeature.Shape); break; default: throw new Exception("Unexpected Geometry Type"); } // Finish drawing on screen. pMxDoc.ActiveView.ScreenDisplay.FinishDrawing(); } /// <summary> /// Flash a line feature on the map /// <param name="pDisplay">The map screen</param> /// <param name="pGeometry">The geometry of the feature to be flashed</param> /// <param name="direction">The digitized direction of the barrier with respect to the underlying source feature</param> /// </summary> private void FlashLine(IScreenDisplay pDisplay, IGeometry pGeometry, esriNetworkEdgeDirection direction) { // The flash will be on a line symbol with an arrow on it ICartographicLineSymbol ipArrowLineSymbol = new CartographicLineSymbolClass(); // the line color will be red IRgbColor ipRgbRedColor = new RgbColorClass(); ipRgbRedColor.Red = 192; // the arrow will be black IRgbColor ipRgbBlackColor = new RgbColorClass(); ipRgbBlackColor.RGB = 0; // set up the arrow that will be displayed along the line IArrowMarkerSymbol ipArrowMarker = new ArrowMarkerSymbolClass(); ipArrowMarker.Style = esriArrowMarkerStyle.esriAMSPlain; ipArrowMarker.Length = 18; ipArrowMarker.Width = 12; ipArrowMarker.Color = ipRgbBlackColor; // set up the line itself ipArrowLineSymbol.Width = 4; ipArrowLineSymbol.Color = ipRgbRedColor; // Set up the Raster Op-Code to help the flash mechanism ((ISymbol)ipArrowMarker).ROP2 = esriRasterOpCode.esriROPNotXOrPen; ((ISymbol)ipArrowLineSymbol).ROP2 = esriRasterOpCode.esriROPNotXOrPen; // decorate the line with the arrow symbol ISimpleLineDecorationElement ipSimpleLineDecorationElement = new SimpleLineDecorationElementClass(); ipSimpleLineDecorationElement.Rotate = true; ipSimpleLineDecorationElement.PositionAsRatio = true; ipSimpleLineDecorationElement.MarkerSymbol = ipArrowMarker; ipSimpleLineDecorationElement.AddPosition(0.5); ILineDecoration ipLineDecoration = new LineDecorationClass(); ipLineDecoration.AddElement(ipSimpleLineDecorationElement); ((ILineProperties)ipArrowLineSymbol).LineDecoration = ipLineDecoration; // the arrow is initially set to correspond to the digitized direction of the line // if the barrier direction is against digitized, then we need to flip the arrow direction if (direction == esriNetworkEdgeDirection.esriNEDAgainstDigitized) ipSimpleLineDecorationElement.FlipAll = true; // Flash the line // Two calls are made to Draw. Since the ROP2 setting is NotXOrPen, the first call // draws the symbol with our new symbology and the second call redraws what was originally // in the place of the symbol pDisplay.SetSymbol(ipArrowLineSymbol as ISymbol); pDisplay.DrawPolyline(pGeometry); System.Threading.Thread.Sleep(300); pDisplay.DrawPolyline(pGeometry); } /// <summary> /// Flash a point feature on the map /// <param name="pDisplay">The map screen</param> /// <param name="pGeometry">The geometry of the feature to be flashed</param> /// </summary> private void FlashPoint(IScreenDisplay pDisplay, IGeometry pGeometry) { // for a point, we only flash a simple circle ISimpleMarkerSymbol pMarkerSymbol = new SimpleMarkerSymbolClass(); pMarkerSymbol.Style = esriSimpleMarkerStyle.esriSMSCircle; // Set up the Raster Op-Code to help the flash mechanism ISymbol pSymbol = pMarkerSymbol as ISymbol; pSymbol.ROP2 = esriRasterOpCode.esriROPNotXOrPen; // Flash the point // Two calls are made to Draw. Since the ROP2 setting is NotXOrPen, the first call // draws the symbol with our new symbology and the second call redraws what was originally // in the place of the symbol pDisplay.SetSymbol(pSymbol); pDisplay.DrawPoint(pGeometry); System.Threading.Thread.Sleep(300); pDisplay.DrawPoint(pGeometry); } #endregion } }
[Visual Basic .NET]
EditorForm.vb
Imports Microsoft.VisualBasic Imports System Imports System.Windows.Forms Imports ESRI.ArcGIS.ArcMapUI Imports ESRI.ArcGIS.Display Imports ESRI.ArcGIS.Framework Imports ESRI.ArcGIS.Geodatabase Imports ESRI.ArcGIS.Geometry Imports ESRI.ArcGIS.NetworkAnalyst Namespace NABarrierLocationEditor Partial Public Class EditorForm Inherits Form #Region "Member Variables" Private Shared ReadOnly EDGE_ALONG As String = "Along Digitized" Private Shared ReadOnly EDGE_AGAINST As String = "Against Digitized" Private m_app As IApplication Private m_context As INAContext Private m_barrier As IFeature #End Region #Region "Initialization" Public Sub New(ByVal app As IApplication, ByVal context As INAContext, ByVal barrier As IFeature) m_barrier = barrier m_app = app m_context = context InitializeComponent() LoadDatagrids() End Sub ''' <summary> ''' Load both the Edge and Junction dataGrids with the information in the barrier feature ''' <param name="barrier">The barrier being loaded into dataGrids</param> ''' </summary> Private Sub LoadDatagrids() ' Populate the cell with the direction drop down CType(dataGridViewEdges.Columns(1), DataGridViewComboBoxColumn).Items.AddRange(EDGE_ALONG, EDGE_AGAINST) ' get the location ranges out of the barrier feature Dim naLocRangesObject As INALocationRangesObject = TryCast(m_barrier, INALocationRangesObject) Dim naLocRanges As INALocationRanges = naLocRangesObject.NALocationRanges If (naLocRanges Is Nothing) Then Throw New Exception("Selected barrier has a null NALocationRanges value") End If ' add all of the junctions included in the barrier to the Junctions dataGrid Dim junctionCount As Integer = naLocRanges.JunctionCount Dim junctionEID As Integer For i As Integer = 0 To junctionCount - 1 naLocRanges.QueryJunction(i, junctionEID) Dim rowIndex As Integer = dataGridViewJunctions.Rows.Add() dataGridViewJunctions.Rows(rowIndex).SetValues(junctionEID) Next i ' add all of the edges included in the barrier to the Edges dataGrid Dim edgeRangeCount As Integer = naLocRanges.EdgeRangeCount Dim edgeEID As Integer Dim fromPosition, toPosition As Double Dim edgeDirection As esriNetworkEdgeDirection For i As Integer = 0 To edgeRangeCount - 1 naLocRanges.QueryEdgeRange(i, edgeEID, edgeDirection, fromPosition, toPosition) Dim directionValue As String = "" If edgeDirection = esriNetworkEdgeDirection.esriNEDAlongDigitized Then directionValue = EDGE_ALONG ElseIf edgeDirection = esriNetworkEdgeDirection.esriNEDAgainstDigitized Then directionValue = EDGE_AGAINST End If dataGridViewEdges.Rows.Add(edgeEID, directionValue, fromPosition, toPosition) Next i End Sub #End Region #Region "Button Clicks" ''' <summary> ''' Occurs when the user clicks the Cancel button. ''' <param name="sender">The control raising this event</param> ''' <param name="e">Arguments associated with the event</param> ''' </summary> Private Sub btnCancel_Click(ByVal sender As Object, ByVal e As EventArgs) Me.Close() End Sub ''' <summary> ''' Occurs when the user clicks the Save button. ''' The barrier information is collected out of the junction and edge barrier ''' dataGrids, then stored back into the original barrier feature as a replacement ''' to the existing barrier information. The original geometry of the barrier remains ''' unaltered. ''' <param name="sender">The control raising this event</param> ''' <param name="e">Arguments associated with the event</param> ''' </summary> Private Sub btnSave_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnSave.Click If (Not ValidateDataGrid(dataGridViewEdges)) Then Return End If If (Not ValidateDataGrid(dataGridViewJunctions)) Then Return End If ' The existing NALocationRanges for the barrier will be replaced with a new one Dim naLocRanges As INALocationRanges = New NALocationRangesClass() ' First gather the edge ranges For Each row As DataGridViewRow In dataGridViewEdges.Rows ' ignore the extra row in the dataGrid If row.IsNewRow Then Continue For End If ' gather the EID value for the new range Dim eid As Integer = Int32.Parse(row.Cells(0).Value.ToString()) ' gather the edge direction value for the new range Dim directionValue As String = row.Cells(1).Value.ToString() Dim direction As esriNetworkEdgeDirection = esriNetworkEdgeDirection.esriNEDNone If directionValue = EDGE_ALONG Then direction = esriNetworkEdgeDirection.esriNEDAlongDigitized ElseIf directionValue = EDGE_AGAINST Then direction = esriNetworkEdgeDirection.esriNEDAgainstDigitized End If ' gather the from and to position values for the new range Dim fromPos As Double = Double.Parse(row.Cells(2).Value.ToString()) Dim toPos As Double = Double.Parse(row.Cells(3).Value.ToString()) ' load the values for this range into the NALocationRanges object naLocRanges.AddEdgeRange(eid, direction, fromPos, toPos) Next row ' Now gather the junctions to be included in the barrier For Each row As DataGridViewRow In dataGridViewJunctions.Rows ' ignore the extra row in the dataGrid If row.IsNewRow Then Continue For End If ' gather the EID value for the junction to include Dim eid As Integer = Int32.Parse(row.Cells(0).Value.ToString()) ' load this junction into the NALocationRanges object naLocRanges.AddJunction(eid) Next row ' Cast the barrier feature to INALocationRanges Object, then populate ' its NALocationRanges value with the new barrier that was created above. ' Then, save the new barrier with a call to Store() Dim naLocationRangesObject As INALocationRangesObject = TryCast(m_barrier, INALocationRangesObject) naLocationRangesObject.NALocationRanges = naLocRanges m_barrier.Store() Me.Close() End Sub ''' <summary> ''' Occurs when the user clicks the Zoom To Barrier Geometry button. ''' The map will zoom to the extent of the Shape of the barrier. ''' <param name="sender">The control raising this event</param> ''' <param name="e">Arguments associated with the event</param> ''' </summary> Private Sub btnZoomToBarrier_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnZoomToBarrier.Click Dim mxDoc As IMxDocument = TryCast(m_app.Document, IMxDocument) mxDoc.ActiveView.Extent = m_barrier.Extent mxDoc.ActiveView.Refresh() End Sub #End Region #Region "Validation" ''' <summary> ''' ValidateDataGrid goes row by row and checks that the values in the grid ''' are valid ''' <param name="dgv">The dataGrid to validate</param> ''' </summary> Private Function ValidateDataGrid(ByVal dgv As DataGridView) As Boolean ' we do all of our validation when the save button is clicked For Each row As DataGridViewRow In dgv.Rows ValidateRow(row) If row.ErrorText <> "" Then dgv.FirstDisplayedScrollingRowIndex = row.Index System.Windows.Forms.MessageBox.Show("You cannot save until all row errors are cleared.", "Barrier Location Editor Warning") Return False End If Next row Return True End Function ''' <summary> ''' ValidateRow checks the cell value in the passed-in row ''' <param name="row">The row to validate</param> ''' <param name="columns">The fields to be validated</param> ''' <param name="datagridviewName">The name of the dataGrid whose row is being validated</param> ''' </summary> Private Sub ValidateRow(ByVal row As DataGridViewRow) ' the extra row to add values does not need to be validated If row.IsNewRow Then Return End If row.ErrorText = "" ' validate each column For Each column As DataGridViewColumn In row.DataGridView.Columns ' none of the column values can be empty If row.Cells(column.Name).Value Is Nothing OrElse row.Cells(column.Name).Value.ToString() = "" Then row.ErrorText = "There cannot be any empty cells" Return End If Dim value As String = row.Cells(column.Name).Value.ToString() Select Case column.Name Case "JunctionEID" If (Not ValidateEID(value, esriNetworkElementType.esriNETJunction)) Then row.ErrorText &= " Junction EID must correspond to a valid network junction" End If Case "EdgeEID" If (Not ValidateEID(value, esriNetworkElementType.esriNETEdge)) Then row.ErrorText &= " Edge EID must correspond to a valid network edge" End If Case "Direction" If value <> "Along Digitized" AndAlso value <> "Against Digitized" Then row.ErrorText &= " Direction must be Along or Against Digitized" End If Case "fromPos" Dim fromPos As Double = -1 If (Not Double.TryParse(value, fromPos)) OrElse fromPos < 0 OrElse fromPos > 1 Then row.ErrorText &= " FromPosition must be a positive number between zero and one" End If Case "toPos" Dim toPos As Double = -1 If (Not Double.TryParse(value, toPos)) OrElse toPos < 0 OrElse toPos > 1 Then row.ErrorText &= " ToPosition must be a positive number between zero and one" End If Case Else Throw New Exception("Unexpected Column") End Select Next column ' Now, validate that the from position is always less than the two position ' FromPosition and ToPosition only matter for edge barriers If row.DataGridView.Name = "dataGridViewEdges" Then If row.Cells("FromPos").Value Is Nothing OrElse row.Cells("ToPos").Value Is Nothing Then row.ErrorText &= " FromPosition and ToPosition must have valid decimal values between 0 and 1" Return End If Dim fromPos As Double = -1 Double.TryParse(row.Cells("FromPos").Value.ToString(), fromPos) Dim toPos As Double = -1 Double.TryParse(row.Cells("ToPos").Value.ToString(), toPos) If fromPos > toPos Then row.ErrorText &= " FromPosition must be equal to or less than the ToPosition" Return End If End If End Sub ''' <summary> ''' Verify that the EID corresponds to a valid network element ''' <param name="value">The EID passed as a string</param> ''' <param name="elementType">The type of element to be verified</param> ''' </summary> Private Function ValidateEID(ByVal value As String, ByVal elementType As esriNetworkElementType) As Boolean ' validate that the EID is a valid integer Dim eid As Integer = -1 If (Not Int32.TryParse(value, eid)) OrElse eid < 1 Then Return False End If ' QueryEdge and QueryJunction will throw exceptions if the EID doesn't match any elements Dim netQuery As INetworkQuery = TryCast(m_context.NetworkDataset, INetworkQuery) Try Select Case elementType Case esriNetworkElementType.esriNETJunction Dim junction As INetworkJunction = TryCast(netQuery.CreateNetworkElement(esriNetworkElementType.esriNETJunction), INetworkJunction) netQuery.QueryJunction(eid, junction) Case esriNetworkElementType.esriNETEdge Dim edge As INetworkEdge = TryCast(netQuery.CreateNetworkElement(esriNetworkElementType.esriNETEdge), INetworkEdge) netQuery.QueryEdge(eid, esriNetworkEdgeDirection.esriNEDAlongDigitized, edge) Case Else Return False End Select Catch Return False End Try Return True End Function #End Region #Region "Flash the Geometry" ''' <summary> ''' Occurs when a dataGrid row header is clicked. ''' It is used here to flash the geometry of the newly selected row ''' <param name="sender">The control raising this event</param> ''' <param name="e">Arguments associated with the event</param> ''' </summary> Private Sub dataGridView_RowHeaderMouseClick(ByVal sender As Object, ByVal e As DataGridViewCellMouseEventArgs) Handles dataGridViewJunctions.RowHeaderMouseClick, dataGridViewEdges.RowHeaderMouseClick ' make sure none of the cells are in edit mode. When in edit mode, the values obtained ' programmatically will not yet match the value the user has changed the cell to Dim dgv As DataGridView = CType(sender, DataGridView) dgv.EndEdit() ' Only flash when there is one selected row If dgv.SelectedRows.Count > 1 Then Return End If Dim selectedRow As DataGridViewRow = dgv.SelectedRows(0) ' If it is the extra dataGrid row or has errors, then don't try to flash it ValidateRow(selectedRow) If selectedRow.IsNewRow OrElse selectedRow.ErrorText <> "" Then Return End If ' also, if any of the row's cell have no value, then don't want to flash it For Each cell As DataGridViewCell In selectedRow.Cells If cell.Value Is Nothing Then Return End If Next cell ' use the EID to obtain the barrier's corresponding network element and source feature Dim element As INetworkElement = GetElementByEID(selectedRow.Cells(0).Value.ToString(), dgv.Name) If element Is Nothing Then Return End If Dim sourceFeature As IFeature = GetSourceFeature(element) ' For an edge, get the part geometry of the barrier covered portion of the source feature ' that should be displayed Dim netEdge As INetworkEdge = TryCast(element, INetworkEdge) Dim displayDirection As esriNetworkEdgeDirection = esriNetworkEdgeDirection.esriNEDNone If Not netEdge Is Nothing Then sourceFeature.Shape = GetBarrierSubcurve(netEdge, sourceFeature, selectedRow) displayDirection = GetDirectionValue(selectedRow) End If ' Draw FlashFeature(sourceFeature, displayDirection) End Sub ''' <summary> ''' Determine the esriNetworkEdgeDirection from the value in the dataGridView cell ''' <param name="selectedRow">The row containing the barrier's location range information</param> ''' </summary> Private Function GetDirectionValue(ByVal selectedRow As DataGridViewRow) As esriNetworkEdgeDirection Dim direction As esriNetworkEdgeDirection = esriNetworkEdgeDirection.esriNEDNone Dim textValue As String = selectedRow.Cells(1).Value.ToString() If textValue = EDGE_ALONG Then direction = esriNetworkEdgeDirection.esriNEDAlongDigitized ElseIf textValue = EDGE_AGAINST Then direction = esriNetworkEdgeDirection.esriNEDAgainstDigitized End If Return direction End Function ''' <summary> ''' Take a network edge, a source feature, and a row from the edges dataGrid, and determine ''' the geometry to be flashed on the map ''' <param name="netEdge">The edge upon which the barrier resides</param> ''' <param name="sourceFeature">The source feature corresponding to the network edge</param> ''' <param name="selectedRow">The row containing the barrier's location range information</param> ''' </summary> Private Function GetBarrierSubcurve(ByVal netEdge As INetworkEdge, ByVal sourceFeature As IFeature, ByVal selectedRow As DataGridViewRow) As ICurve ' value for displaying the entire source feature Dim fromPosition As Double = 0 Dim toPosition As Double = 1 ' Find the values for displaying only the element portion of the source feature Dim fromElementPosition, toElementPosition As Double netEdge.QueryPositions(fromElementPosition, toElementPosition) ' due to the element possibly being in the against digitized direction, ' fromPosition could be greater than toPosition. If that is the case, swap the values If fromElementPosition > toElementPosition Then Dim tmp As Double = fromElementPosition fromElementPosition = toElementPosition toElementPosition = tmp End If Dim direction As esriNetworkEdgeDirection = GetDirectionValue(selectedRow) If direction = esriNetworkEdgeDirection.esriNEDNone Then Return Nothing End If ' Flash the edge If rbFlashElementPortion.Checked Then fromPosition = fromElementPosition toPosition = toElementPosition ' Flash the barrier portion of the edge ElseIf rbFlashBarrierPortion.Checked Then Dim fromBarrierPosition As Double = -1 Dim toBarrierPosition As Double = -1 ' gather the from and to position values for the barrier fromBarrierPosition = Double.Parse(selectedRow.Cells(2).Value.ToString()) toBarrierPosition = Double.Parse(selectedRow.Cells(3).Value.ToString()) ' for barriers in the against direction, we need to adjust that the element position is If direction = esriNetworkEdgeDirection.esriNEDAgainstDigitized Then fromBarrierPosition = 1 - fromBarrierPosition toBarrierPosition = 1 - toBarrierPosition End If ' use the positioning along the element of the barrier ' to get the position along the original source feature fromPosition = fromElementPosition + (fromBarrierPosition * (toElementPosition - fromElementPosition)) toPosition = fromElementPosition + (toBarrierPosition * (toElementPosition - fromElementPosition)) End If If fromPosition > toPosition Then Dim tmp As Double = fromPosition fromPosition = toPosition toPosition = tmp End If ' get the subspan on the polyline that represents the from and to positions we specified Dim displayCurve As ICurve = Nothing Dim sourceCurve As ICurve = TryCast(sourceFeature.Shape, ICurve) sourceCurve.GetSubcurve(fromPosition, toPosition, True, displayCurve) Return displayCurve End Function ''' <summary> ''' Take an EID value as a string from one of the dataGridView controls and find ''' the network element that corresponds to the EID ''' <param name="eidString">The EID value as a string</param> ''' <param name="datagridviewName">The name of the dataGrid that held the EID</param> ''' </summary> Private Function GetElementByEID(ByVal eidString As String, ByVal datagridviewName As String) As INetworkElement Dim eid As Integer = -1 If (Not Int32.TryParse(eidString, eid)) Then Return Nothing End If Dim netQuery As INetworkQuery = TryCast(m_context.NetworkDataset, INetworkQuery) Dim edge As INetworkEdge = TryCast(netQuery.CreateNetworkElement(esriNetworkElementType.esriNETEdge), INetworkEdge) Dim junction As INetworkJunction = TryCast(netQuery.CreateNetworkElement(esriNetworkElementType.esriNETJunction), INetworkJunction) Dim element As INetworkElement = Nothing Try ' Populate the network element from the EID If datagridviewName = "dataGridViewEdges" Then netQuery.QueryEdge(eid, esriNetworkEdgeDirection.esriNEDAlongDigitized, edge) element = TryCast(edge, INetworkElement) ElseIf datagridviewName = "dataGridViewJunctions" Then netQuery.QueryJunction(eid, junction) element = TryCast(junction, INetworkElement) End If Catch ' if the query fails, the element will not be displayed End Try Return element End Function ''' <summary> ''' Take a network element and return its corresponding source feature ''' <param name="element">The return source feature corresponds to this element</param> ''' </summary> Private Function GetSourceFeature(ByVal element As INetworkElement) As IFeature ' To draw the network element, we will need its corresponding source feature information ' Get the sourceID and OID from the element Dim sourceID As Integer = element.SourceID Dim sourceOID As Integer = element.OID ' Get the source feature from the network source Dim netSource As INetworkSource = m_context.NetworkDataset.SourceByID(sourceID) Dim fClassContainer As IFeatureClassContainer = TryCast(m_context.NetworkDataset, IFeatureClassContainer) Dim sourceFClass As IFeatureClass = fClassContainer.ClassByName(netSource.Name) Return sourceFClass.GetFeature(sourceOID) End Function ''' <summary> ''' Flash the feature geometry on the map ''' <param name="pFeature">The feature being flashed</param> ''' <param name="pMxDoc">A hook to the application display</param> ''' <param name="direction">The digitized direction of the barrier with respect to the underlying source feature</param> ''' </summary> Private Sub FlashFeature(ByVal pFeature As IFeature, ByVal direction As esriNetworkEdgeDirection) Dim pMxDoc As IMxDocument = TryCast(m_app.Document, IMxDocument) ' Start drawing on screen. pMxDoc.ActiveView.ScreenDisplay.StartDrawing(0, CShort(Fix(esriScreenCache.esriNoScreenCache))) ' Switch functions based on Geometry type. Select Case pFeature.Shape.GeometryType Case esriGeometryType.esriGeometryPolyline FlashLine(pMxDoc.ActiveView.ScreenDisplay, pFeature.Shape, direction) Case esriGeometryType.esriGeometryPolygon ' no network elements can be polygons Case esriGeometryType.esriGeometryPoint FlashPoint(pMxDoc.ActiveView.ScreenDisplay, pFeature.Shape) Case Else Throw New Exception("Unexpected Geometry Type") End Select ' Finish drawing on screen. pMxDoc.ActiveView.ScreenDisplay.FinishDrawing() End Sub ''' <summary> ''' Flash a line feature on the map ''' <param name="pDisplay">The map screen</param> ''' <param name="pGeometry">The geometry of the feature to be flashed</param> ''' <param name="direction">The digitized direction of the barrier with respect to the underlying source feature</param> ''' </summary> Private Sub FlashLine(ByVal pDisplay As IScreenDisplay, ByVal pGeometry As IGeometry, ByVal direction As esriNetworkEdgeDirection) ' The flash will be on a line symbol with an arrow on it Dim ipArrowLineSymbol As ICartographicLineSymbol = New CartographicLineSymbolClass() ' the line color will be red Dim ipRgbRedColor As IRgbColor = New RgbColorClass() ipRgbRedColor.Red = 192 ' the arrow will be black Dim ipRgbBlackColor As IRgbColor = New RgbColorClass() ipRgbBlackColor.RGB = 0 ' set up the arrow that will be displayed along the line Dim ipArrowMarker As IArrowMarkerSymbol = New ArrowMarkerSymbolClass() ipArrowMarker.Style = esriArrowMarkerStyle.esriAMSPlain ipArrowMarker.Length = 18 ipArrowMarker.Width = 12 ipArrowMarker.Color = ipRgbBlackColor ' set up the line itself ipArrowLineSymbol.Width = 4 ipArrowLineSymbol.Color = ipRgbRedColor ' Set up the Raster Op-Code to help the flash mechanism CType(ipArrowMarker, ISymbol).ROP2 = esriRasterOpCode.esriROPNotXOrPen CType(ipArrowLineSymbol, ISymbol).ROP2 = esriRasterOpCode.esriROPNotXOrPen ' decorate the line with the arrow symbol Dim ipSimpleLineDecorationElement As ISimpleLineDecorationElement = New SimpleLineDecorationElementClass() ipSimpleLineDecorationElement.Rotate = True ipSimpleLineDecorationElement.PositionAsRatio = True ipSimpleLineDecorationElement.MarkerSymbol = ipArrowMarker ipSimpleLineDecorationElement.AddPosition(0.5) Dim ipLineDecoration As ILineDecoration = New LineDecorationClass() ipLineDecoration.AddElement(ipSimpleLineDecorationElement) CType(ipArrowLineSymbol, ILineProperties).LineDecoration = ipLineDecoration ' the arrow is initially set to correspond to the digitized direction of the line ' if the barrier direction is against digitized, then we need to flip the arrow direction If direction = esriNetworkEdgeDirection.esriNEDAgainstDigitized Then ipSimpleLineDecorationElement.FlipAll = True End If ' Flash the line ' Two calls are made to Draw. Since the ROP2 setting is NotXOrPen, the first call ' draws the symbol with our new symbology and the second call redraws what was originally ' in the place of the symbol pDisplay.SetSymbol(TryCast(ipArrowLineSymbol, ISymbol)) pDisplay.DrawPolyline(pGeometry) System.Threading.Thread.Sleep(300) pDisplay.DrawPolyline(pGeometry) End Sub ''' <summary> ''' Flash a point feature on the map ''' <param name="pDisplay">The map screen</param> ''' <param name="pGeometry">The geometry of the feature to be flashed</param> ''' </summary> Private Sub FlashPoint(ByVal pDisplay As IScreenDisplay, ByVal pGeometry As IGeometry) ' for a point, we only flash a simple circle Dim pMarkerSymbol As ISimpleMarkerSymbol = New SimpleMarkerSymbolClass() pMarkerSymbol.Style = esriSimpleMarkerStyle.esriSMSCircle ' Set up the Raster Op-Code to help the flash mechanism Dim pSymbol As ISymbol = TryCast(pMarkerSymbol, ISymbol) pSymbol.ROP2 = esriRasterOpCode.esriROPNotXOrPen ' Flash the point ' Two calls are made to Draw. Since the ROP2 setting is NotXOrPen, the first call ' draws the symbol with our new symbology and the second call redraws what was originally ' in the place of the symbol pDisplay.SetSymbol(pSymbol) pDisplay.DrawPoint(pGeometry) System.Threading.Thread.Sleep(300) pDisplay.DrawPoint(pGeometry) End Sub #End Region End Class End Namespace