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