ArcObjects Library Reference  

BisectorRule

About the Implementing the ISchematicRulesHelper to easily develop a custom schematic rule Sample

[C#]

BisectorRule.cs

using ESRI.ArcGIS;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Schematic;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace CustomRulesCS
{
    [ClassInterface(ClassInterfaceType.None)]
    [Guid(BisectorRule.GUID)]
    [ProgId(BisectorRule.PROGID)]
    public class BisectorRule : ISchematicRule, ISchematicRuleDesign
    {
        public const string GUID = "31C4B848-34AF-4369-97FA-CDD985BF0592";
        public const string PROGID = "CustomRulesCS.BisectorRule";

        // Register/unregister categories for this class
        #region "Component Category Registration"
        [System.Runtime.InteropServices.ComRegisterFunction()]
        public static void Register(string CLSID)
        {
            SchematicRules.Register(CLSID);
        }

        [System.Runtime.InteropServices.ComUnregisterFunction()]
        public static void Unregister(string CLSID)
        {
            SchematicRules.Unregister(CLSID);
        }
        #endregion

        private ISchematicDiagramClass m_diagramClass;
        private double m_distance = 0.5;
        private string m_description = "Bisector Rule C#";
        private string m_parentNodeClassName;
        private string m_targetNodeClassName;
        private string m_targetLinkClassName;

        private ISchematicInMemoryFeatureClass m_parentNodeClass;
        private ISchematicInMemoryFeatureClass m_targetNodeClass;
        private ISchematicInMemoryFeatureClass m_targetLinkClass;
        private const string Separator = "_";
        private const string extensionName = "BisectorRuleCS";


        public BisectorRule()
        {
        }

        ~BisectorRule()
        {
            m_diagramClass = null;
            m_parentNodeClass = null;
            m_targetNodeClass = null;
            m_targetLinkClass = null;
        }

        public double distance
        {
            get
            {
                return m_distance;
            }
            set
            {
                m_distance = value;
            }
        }

        public string parentNodeClassName
        {
            get
            {
                return m_parentNodeClassName;
            }
            set
            {
                m_parentNodeClassName = value;
            }
        }

        public string targetNodeClassName
        {
            get
            {
                return m_targetNodeClassName;
            }
            set
            {
                m_targetNodeClassName = value;
            }
        }

        public string targetLinkClassName
        {
            get
            {
                return m_targetLinkClassName;
            }
            set
            {
                m_targetLinkClassName = value;
            }
        }

        private string GetUniqueName(ISchematicInMemoryDiagram inMemoryDiagram, esriSchematicElementType elementType, string featureName)
        {
            string nameUnique = featureName;

            ISchematicInMemoryFeature schInMemoryfeature = null;
            int index = 1;
            bool endWhile = false;
            while (!endWhile)
            {
                schInMemoryfeature = inMemoryDiagram.GetSchematicInMemoryFeatureByType(elementType, nameUnique);
                if (schInMemoryfeature == null)
                {
                    endWhile = true;
                }
                else if (schInMemoryfeature.Displayed)
                {
                    nameUnique = nameUnique + index.ToString();
                    index++;
                }
                else
                {
                    endWhile = true;
                }
            }

            return nameUnique;
        }

        private void AddNodesDegreeTwo(IEnumSchematicInMemoryFeature enumInMemoryFeature,
                Dictionary<string, ISchematicInMemoryFeature> colSchfeatureNode, ISchematicRulesHelper rulesHelper)
        {
            ISchematicInMemoryFeature schInMemoryfeature;

            if (enumInMemoryFeature == null || colSchfeatureNode == null || rulesHelper == null) 
                return;

            enumInMemoryFeature.Reset();
            schInMemoryfeature = enumInMemoryFeature.Next();
            while (schInMemoryfeature != null)
            {
                if (schInMemoryfeature.Displayed)
                {
                    IEnumSchematicInMemoryFeature enumLinks = rulesHelper.GetDisplayedIncidentLinks((ISchematicInMemoryFeatureNode)schInMemoryfeature, esriSchematicEndPointType.esriSchematicOriginOrExtremityNode);
                    if (enumLinks != null && enumLinks.Count == 2)
                    {
                        if (!colSchfeatureNode.ContainsKey(schInMemoryfeature.Name))
                            colSchfeatureNode.Add(schInMemoryfeature.Name, schInMemoryfeature);
                    }
                }

                schInMemoryfeature = enumInMemoryFeature.Next();
            }
        }

        private ISchematicInMemoryFeatureClass GetSchematicInMemoryFeatureClass(ISchematicInMemoryDiagram inMemoryDiagram, ISchematicElementClass eltClass)
        {
            ISchematicInMemoryFeatureClassContainer SchInMemoryFeatureClassContainer = (ISchematicInMemoryFeatureClassContainer)inMemoryDiagram;
            return SchInMemoryFeatureClassContainer.GetSchematicInMemoryFeatureClass(eltClass);
        }

        private double CalculateAngle(IPoint pointFrom, IPoint pointTo)
        {
            const double radToDegre = 180 / Math.PI;
            double dX, dY, angle;
            angle = 0;

            dX = pointTo.X - pointFrom.X;
            dY = pointTo.Y - pointFrom.Y;

            if (dX != 0)
                angle = Math.Atan(dY / dX) * radToDegre;
            else // case 2 points are same abcisse
            {
                if (dY < 0)
                {
                    angle = 270;
                    return angle;
                }
                else
                {
                    angle = 90;
                    return angle;
                }
            }

            if (dX < 0)
                angle = angle + 180;

            if (angle < 0)
                angle = angle + 360;

            return angle;
        }

        private double CalculateAngleBisector(double angle1, double angle2)
        {
            double angleFinal;
            if (Math.Abs(angle1 - angle2) > 180)
                angleFinal = (angle1 + angle2) / 2 + 180;
            else
                angleFinal = (angle1 + angle2) / 2;

            return angleFinal;
        }

        private IPoint GetCoordPointBisector(IPoint pointOrigine, double degreeBiSectore, double distance)
        {
            IPoint pointBisector = new PointClass();
            double angleBisector, degreeFinal;
            double dX, dY;
            int casAngle = 0;
            if (degreeBiSectore <= 90)
            {
                degreeFinal = degreeBiSectore;
                casAngle = 1;

            }
            else if (degreeBiSectore <= 180)
            {
                degreeFinal = 180 - degreeBiSectore;
                casAngle = 2;
            }
            else if (degreeBiSectore <= 270)
            {
                degreeFinal = degreeBiSectore - 180;
                casAngle = 3;
            }
            else
            {
                degreeFinal = 360 - degreeBiSectore;
                casAngle = 4;
            }

            angleBisector = Math.PI * degreeFinal / 180.0;

            dY = distance * Math.Sin(angleBisector);
            dX = distance * Math.Cos(angleBisector);

            switch (casAngle)
            {
                case 1: // case which the vector is on the first of the circle
                    pointBisector.X = pointOrigine.X + dX;
                    pointBisector.Y = pointOrigine.Y + dY;
                    break;
                case 2: // case which the vector is on the second of the circle
                    pointBisector.X = pointOrigine.X - dX;
                    pointBisector.Y = pointOrigine.Y + dY;
                    break;
                case 3: // case which the vector is on the thirst of the circle
                    pointBisector.X = pointOrigine.X - dX;
                    pointBisector.Y = pointOrigine.Y - dY;
                    break;
                default: // case which the vector is on the fourth of the circle
                    pointBisector.X = pointOrigine.X + dX;
                    pointBisector.Y = pointOrigine.Y - dY;
                    break;
            }

            return pointBisector;
        }

        #region ISchematicRule Members

        public void Alter(ISchematicDiagramClass schematicDiagramClass, ESRI.ArcGIS.esriSystem.IPropertySet propertySet)
        {
            m_diagramClass = schematicDiagramClass;

            try
            {
                m_description = propertySet.GetProperty("DESCRIPTION").ToString();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message, "property DESCRIPTION");
            }

            try
            {
                m_parentNodeClassName = propertySet.GetProperty("PARENTNODECLASS").ToString();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message, "property PARENTNODECLASS");
            }
            try
            {
                m_targetNodeClassName = propertySet.GetProperty("TARGETNODECLASS").ToString();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message, "property TARGETNODECLASS");
            }

            try
            {
                m_targetLinkClassName = propertySet.GetProperty("TARGETLINKCLASS").ToString();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message, "property TARGETLINKCLASS");
            }

            try
            {
                m_distance = (double)propertySet.GetProperty("DISTANCE");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message, "property DISTANCE");
            }

        }

        public void Apply(ISchematicInMemoryDiagram inMemoryDiagram, ESRI.ArcGIS.esriSystem.ITrackCancel cancelTracker)
        {
            ISchematicRulesHelper rulesHelper = new SchematicRulesHelperClass();
            System.Collections.Generic.Dictionary<string, ISchematicInMemoryFeature> colSchfeatureNode = new Dictionary<string, ISchematicInMemoryFeature>();
            rulesHelper.InitHelper(inMemoryDiagram);
            rulesHelper.KeepVertices = true;

            ISchematicDiagramClass diagramClass = null;
            ISchematicElementClass elementClass;
            ISchematicElementClass elementClassParentNode = null;
            IEnumSchematicElementClass enumSchEltCls;

            try
            {
                diagramClass = inMemoryDiagram.SchematicDiagramClass;
            }
            catch { }

            if (diagramClass == null) return;

            enumSchEltCls = diagramClass.AssociatedSchematicElementClasses;

            if (enumSchEltCls.Count == 0) return;

            enumSchEltCls.Reset();
            elementClass = enumSchEltCls.Next();

            while (elementClass != null)
            {
                if (elementClass.Name == m_parentNodeClassName)
                {
                    elementClassParentNode = elementClass;
                    m_parentNodeClass = GetSchematicInMemoryFeatureClass(inMemoryDiagram, elementClass);
                }

                if (elementClass.Name == m_targetNodeClassName)
                {
                    m_targetNodeClass = GetSchematicInMemoryFeatureClass(inMemoryDiagram, elementClass);
                }

                if (elementClass.Name == m_targetLinkClassName)
                {
                    m_targetLinkClass = GetSchematicInMemoryFeatureClass(inMemoryDiagram, elementClass);
                }

                elementClass = enumSchEltCls.Next();
            }

            if (m_parentNodeClass == null || m_targetNodeClass == null || m_targetLinkClass == null)
                return;

            IEnumSchematicInMemoryFeature enumSchematicInMemoryFeature;
            // list nodes degree two
            // get all feature of parent node class
            enumSchematicInMemoryFeature = inMemoryDiagram.GetSchematicInMemoryFeaturesByClass(elementClassParentNode);
            enumSchematicInMemoryFeature.Reset();

            // add the node into collection if it contains only 2 links displayed.
            AddNodesDegreeTwo(enumSchematicInMemoryFeature, colSchfeatureNode, rulesHelper);
            ISchematicInMemoryFeature schFeatureParent;

            foreach (KeyValuePair<string, ISchematicInMemoryFeature> kvp in colSchfeatureNode)
            {
                schFeatureParent = colSchfeatureNode[kvp.Key];

                if (schFeatureParent == null)
                    continue;

                // get 2 links connected of eache feature node
                IEnumSchematicInMemoryFeature enumLinks = rulesHelper.GetDisplayedIncidentLinks((ISchematicInMemoryFeatureNode)schFeatureParent, esriSchematicEndPointType.esriSchematicOriginOrExtremityNode);
                // enumLinks surely not null    and it contain 2 links displayed

                double angle1, angle2, angleBisector;
                bool first = true;

                angle1 = angle2 = angleBisector = 0;
                IPoint pointParent = null;
                ISchematicInMemoryFeatureNodeGeometry geoParent;
                geoParent = (ISchematicInMemoryFeatureNodeGeometry)schFeatureParent;

                pointParent = geoParent.InitialPosition;
                IPoint pointSon = null;
                bool enableCalculate = true;

                ISchematicInMemoryFeature schInMemoryFeature = enumLinks.Next();

                ISchematicInMemoryFeatureLink schInMemoryLink = (ISchematicInMemoryFeatureLink)schInMemoryFeature;
                while (schInMemoryLink != null)
                {
                    ISchematicInMemoryFeatureNodeGeometry nodeGeo;
                    // get angle of 2 links connected
                    if (schInMemoryLink.FromNode.Name == schFeatureParent.Name)
                        nodeGeo = (ISchematicInMemoryFeatureNodeGeometry)schInMemoryLink.ToNode;
                    else
                        nodeGeo = (ISchematicInMemoryFeatureNodeGeometry)schInMemoryLink.FromNode;

                    if (nodeGeo == null)
                    {
                        enableCalculate = false;
                        break;
                    }

                    pointSon = nodeGeo.InitialPosition;
                    if (first)
                    {
                        angle1 = CalculateAngle(pointParent, pointSon);
                        first = false;
                    }
                    else
                        angle2 = CalculateAngle(pointParent, pointSon);

                    schInMemoryFeature = enumLinks.Next();
                    schInMemoryLink = (ISchematicInMemoryFeatureLink)schInMemoryFeature;
                }

                // caculate angle bisector
                if (enableCalculate)
                    angleBisector = CalculateAngleBisector(angle1, angle2);
                else
                    continue;

                // construct a geometry for the new node node
                // now call alterNode to create a new schematic feature
                // construct a correct name
                string uniqueNodeName, featureCreateName;
                featureCreateName = schFeatureParent.Name + Separator + extensionName;
                esriSchematicElementType elementType = esriSchematicElementType.esriSchematicNodeType;
                uniqueNodeName = GetUniqueName(inMemoryDiagram, elementType, featureCreateName);
                IWorkspace workspace = null;

                try
                {
                    workspace = inMemoryDiagram.SchematicDiagramClass.SchematicDataset.SchematicWorkspace.Workspace;
                }
                catch { }

                int datasourceID = -1;
                
                if(workspace != null)
                    datasourceID = rulesHelper.FindDataSourceID(workspace, false);

                if(datasourceID == -1)
                    datasourceID = m_diagramClass.SchematicDataset.DefaultSchematicDataSource.ID;

                ISchematicInMemoryFeature schFeatureNodeCreate = null;
                IPoint pointBisector = null;
                pointBisector = GetCoordPointBisector(pointParent, angleBisector, m_distance);
                try
                {
                    schFeatureNodeCreate = rulesHelper.AlterNode(m_targetNodeClass, uniqueNodeName, null, (IGeometry)pointBisector, datasourceID, 0);
                }
                catch(Exception ex)
                {
                    System.Diagnostics.Trace.WriteLine(ex.Message, "Impossible to create a feature Node");
                }

                // now construct a unique link name
                string linkName = schFeatureParent.Name + Separator + uniqueNodeName;
                string uniqueLinkName;

                elementType = esriSchematicElementType.esriSchematicLinkType;
                uniqueLinkName = GetUniqueName(inMemoryDiagram, elementType, linkName);
                // construct a link
                ISchematicInMemoryFeature schFeatureLinkCreate = null;
                try
                {
                    schFeatureLinkCreate = rulesHelper.AlterLink(m_targetLinkClass, uniqueLinkName, null, null, datasourceID, 0, schFeatureParent.Name, uniqueNodeName, esriFlowDirection.esriFDWithFlow, 0, 0);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Trace.WriteLine(ex.Message, "Impossible to create a feature link");
                }
            }

            if (colSchfeatureNode.Count > 0)
                colSchfeatureNode.Clear();

            colSchfeatureNode = null;
            rulesHelper = null;
        }

        public ESRI.ArcGIS.esriSystem.UID ClassID
        {
            get
            {
                ESRI.ArcGIS.esriSystem.UID ruleID = new ESRI.ArcGIS.esriSystem.UID();
                ruleID.Value = PROGID;
                return ruleID;
            }
        }

        public string Description
        {
            get
            {
                return m_description;
            }
            set
            {
                m_description = value;
            }
        }

        string ISchematicRule.Description
        {
            get { return m_description; }
        }

        public string Name
        {
            get
            {
                return "Bisector Rule C#";
            }
        }

        public ESRI.ArcGIS.esriSystem.IPropertySet PropertySet
        {
            get
            {
                ESRI.ArcGIS.esriSystem.IPropertySet propertySet = new ESRI.ArcGIS.esriSystem.PropertySet();
                propertySet.SetProperty("DESCRIPTION", m_description);
                propertySet.SetProperty("PARENTNODECLASS", m_parentNodeClassName);
                propertySet.SetProperty("TARGETNODECLASS", m_targetNodeClassName);
                propertySet.SetProperty("TARGETLINKCLASS", m_targetLinkClassName);
                propertySet.SetProperty("DISTANCE", m_distance);

                return propertySet;
            }
        }

        ISchematicDiagramClass ISchematicRule.SchematicDiagramClass
        {
            get { return m_diagramClass; }
        }

        #endregion

        #region ISchematicRuleDesign Members

        public void Detach()
        {
            m_diagramClass = null;
            m_parentNodeClass = null;
            m_targetNodeClass = null;
            m_targetLinkClass = null;
        }

        ESRI.ArcGIS.esriSystem.IPropertySet ISchematicRuleDesign.PropertySet
        {
            set
            {
                m_description = value.GetProperty("DESCRIPTION").ToString();
                m_parentNodeClassName = value.GetProperty("PARENTNODECLASS").ToString();
                m_targetNodeClassName = value.GetProperty("TARGETNODECLASS").ToString();
                m_targetLinkClassName = value.GetProperty("TARGETLINKCLASS").ToString();
                m_distance = (double)value.GetProperty("DISTANCE");
            }
        }

        ISchematicDiagramClass ISchematicRuleDesign.SchematicDiagramClass
        {
            get
            {
                return m_diagramClass;
            }
            set
            {
                m_diagramClass = value;
            }
        }
        #endregion ISchematicRuleDesign


    }
}

[Visual Basic .NET]

BisectorRule.vb

Option Strict Off

Imports System
Imports System.Collections.Generic
Imports System.Runtime.InteropServices
Imports ESRI.ArcGIS
Imports ESRI.ArcGIS.ADF.CATIDs
Imports ESRI.ArcGIS.Schematic
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.Geodatabase
Imports esriSystem = ESRI.ArcGIS.esriSystem



<System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)> _
<System.Runtime.InteropServices.Guid(BisectorRule.GUID)> _
<System.Runtime.InteropServices.ProgId(BisectorRule.PROGID)> _
Public Class BisectorRule
    Implements ESRI.ArcGIS.Schematic.ISchematicRule
    Implements ESRI.ArcGIS.Schematic.ISchematicRuleDesign
    Public Const GUID As String = "36D59619-86EB-4244-A521-CEF2187EABCC"
    Public Const PROGID As String = "CustomRulesVB.BisectorRule"

    ' Register/unregister categories for this class
#Region "Component Category Registration"
    <System.Runtime.InteropServices.ComRegisterFunction()> _
    Shared Sub Register(ByVal CLSID As String)
        ESRI.ArcGIS.ADF.CATIDs.SchematicRules.Register(CLSID)
    End Sub

    <System.Runtime.InteropServices.ComUnregisterFunction()> _
    Shared Sub Unregister(ByVal CLSID As String)
        ESRI.ArcGIS.ADF.CATIDs.SchematicRules.Unregister(CLSID)
    End Sub
#End Region

    Private m_diagramClass As ESRI.ArcGIS.Schematic.ISchematicDiagramClass
    Private m_distance As Double = 0.5
    Private m_parentNodeClassName As String
    Private m_targetNodeClassName As String
    Private m_targetLinkClassName As String
    Private m_description As String = "Bisector Rule VBNet"
    Private m_parentNodeClass As ISchematicInMemoryFeatureClass
    Private m_targetNodeClass As ISchematicInMemoryFeatureClass
    Private m_targetLinkClass As ISchematicInMemoryFeatureClass
    Private Const Separator As String = "_"
    Private Const extensionName As String = "BisectorRuleVB"


    Public Sub New()
    End Sub

    Protected Overrides Sub Finalize()
        m_diagramClass = Nothing
        m_parentNodeClass = Nothing
        m_targetNodeClass = Nothing
        m_targetLinkClass = Nothing
        MyBase.Finalize()
    End Sub

    Public Property Distance() As Double
        Get
            Return m_distance
        End Get
        Set(ByVal value As Double)
            m_distance = value
        End Set
    End Property

    Public Property parentNodeClassName() As String
        Get
            Return m_parentNodeClassName
        End Get
        Set(ByVal value As String)
            m_parentNodeClassName = value
        End Set
    End Property

    Public Property targetNodeClassName() As String
        Get
            Return m_targetNodeClassName
        End Get
        Set(ByVal value As String)
            m_targetNodeClassName = value
        End Set
    End Property

    Public Property targetLinkClassName() As String
        Get
            Return m_targetLinkClassName
        End Get
        Set(ByVal value As String)
            m_targetLinkClassName = value
        End Set
    End Property

#Region "ISchematicRule Members"
    Public Sub Alter(ByVal schematicDiagramClass As ESRI.ArcGIS.Schematic.ISchematicDiagramClass, ByVal propertySet As ESRI.ArcGIS.esriSystem.IPropertySet) Implements ESRI.ArcGIS.Schematic.ISchematicRule.Alter
        m_diagramClass = schematicDiagramClass

        Try
            m_description = propertySet.GetProperty("DESCRIPTION").ToString()
        Catch ex As System.Exception
            System.Diagnostics.Trace.WriteLine(ex.Message, "property DESCRIPTION")
        End Try

        Try
            m_parentNodeClassName = propertySet.GetProperty("PARENTNODECLASS").ToString()
        Catch ex As System.Exception
            System.Diagnostics.Trace.WriteLine(ex.Message, "property PARENTNODECLASS")
        End Try

        Try
            m_targetNodeClassName = propertySet.GetProperty("TARGETNODECLASS").ToString()
        Catch ex As System.Exception
            System.Diagnostics.Trace.WriteLine(ex.Message, "property TARGETNODECLASS")
        End Try

        Try
            m_targetLinkClassName = propertySet.GetProperty("TARGETLINKCLASS").ToString()
        Catch ex As System.Exception
            System.Diagnostics.Trace.WriteLine(ex.Message, "property TARGETLINKCLASS")
        End Try

        Try
            m_distance = CType(propertySet.GetProperty("DISTANCE"), Double)
        Catch ex As System.Exception
            System.Diagnostics.Trace.WriteLine(ex.Message, "property DISTANCE")
        End Try

    End Sub


    Public Sub Apply(ByVal inMemoryDiagram As ESRI.ArcGIS.Schematic.ISchematicInMemoryDiagram, Optional ByVal cancelTracker As ESRI.ArcGIS.esriSystem.ITrackCancel = Nothing) Implements ESRI.ArcGIS.Schematic.ISchematicRule.Apply
        Dim rulesHelper As ISchematicRulesHelper = New SchematicRulesHelper()
        Dim diagramClass As ISchematicDiagramClass = Nothing
        Dim elementClass As ISchematicElementClass
        Dim enumElementClass As IEnumSchematicElementClass
        Dim elementClassParentNode As ISchematicElementClass = Nothing

        rulesHelper.InitHelper(inMemoryDiagram)
        rulesHelper.KeepVertices = True
        Dim colSchfeatureNode As New System.Collections.Generic.Dictionary(Of String, ISchematicInMemoryFeature)
        Try
            diagramClass = inMemoryDiagram.SchematicDiagramClass
        Catch
        End Try

        If (diagramClass Is Nothing) Then Exit Sub
        enumElementClass = diagramClass.AssociatedSchematicElementClasses

        If (enumElementClass Is Nothing) Then Exit Sub

        enumElementClass.Reset()
        elementClass = enumElementClass.Next()
        While (elementClass IsNot Nothing)
            If (elementClass.Name = m_parentNodeClassName) Then
                elementClassParentNode = elementClass
                m_parentNodeClass = GetSchematicInMemoryFeatureClass(inMemoryDiagram, elementClass)
            End If

            If (elementClass.Name = m_targetNodeClassName) Then
                m_targetNodeClass = GetSchematicInMemoryFeatureClass(inMemoryDiagram, elementClass)
            End If

            If (elementClass.Name = m_targetLinkClassName) Then
                m_targetLinkClass = GetSchematicInMemoryFeatureClass(inMemoryDiagram, elementClass)
            End If

            elementClass = enumElementClass.Next()
        End While

        If (m_parentNodeClass Is Nothing Or m_targetNodeClass Is Nothing Or m_targetLinkClass Is Nothing) Then
            Exit Sub
        End If

        Dim enumSchematicInMemoryFeature As IEnumSchematicInMemoryFeature = Nothing

        ' list nodes degree two
        ' get all feature of parent node class


        enumSchematicInMemoryFeature = inMemoryDiagram.GetSchematicInMemoryFeaturesByClass(elementClassParentNode)
        enumSchematicInMemoryFeature.Reset()

        'add the node into collection if it contains only 2 links displayed.
        AddNodesDegreeTwo(enumSchematicInMemoryFeature, colSchfeatureNode, rulesHelper)

        Dim schFeatureParent As ISchematicInMemoryFeature
        Dim kvp As KeyValuePair(Of String, ISchematicInMemoryFeature)
        For Each kvp In colSchfeatureNode
            schFeatureParent = CType(colSchfeatureNode(kvp.Key), ISchematicInMemoryFeature)
            If (schFeatureParent Is Nothing) Then Continue For
            'get 2 links connected of eache feature node
            Dim enumLinks As IEnumSchematicInMemoryFeature

            enumLinks = rulesHelper.GetDisplayedIncidentLinks(CType(schFeatureParent, ISchematicInMemoryFeatureNode), esriSchematicEndPointType.esriSchematicOriginOrExtremityNode)
            'enumLinks surely not null and it contain 2 links displayed

            Dim angle1, angle2, angleBisector As Double
            angle1 = 0
            angle2 = 0
            angleBisector = 0
            Dim first As Boolean = True
            Dim pointParent As IPoint
            Dim geoParent As ISchematicInMemoryFeatureNodeGeometry
            geoParent = CType(schFeatureParent, ISchematicInMemoryFeatureNodeGeometry)

            pointParent = geoParent.InitialPosition
            Dim pointSon As IPoint = Nothing
            Dim schInMemoryFeature As ISchematicInMemoryFeature = enumLinks.Next()

            Dim schInMemoryLink As ISchematicInMemoryFeatureLink = CType(schInMemoryFeature, ISchematicInMemoryFeatureLink)
            Dim enableCalculate As Boolean = True
            While (schInMemoryLink IsNot Nothing)

                Dim nodeGeo As ISchematicInMemoryFeatureNodeGeometry
                'get angle of 2 links connected
                If (schInMemoryLink.FromNode Is schFeatureParent) Then
                    nodeGeo = CType(schInMemoryLink.ToNode, ISchematicInMemoryFeatureNodeGeometry)
                Else
                    nodeGeo = CType(schInMemoryLink.FromNode, ISchematicInMemoryFeatureNodeGeometry)
                End If

                If (nodeGeo Is Nothing) Then
                    enableCalculate = False
                    Exit While
                End If

                pointSon = nodeGeo.InitialPosition
                If (first) Then
                    angle1 = CalculateAngle(pointParent, pointSon)
                    first = False
                Else
                    angle2 = CalculateAngle(pointParent, pointSon)
                End If

                schInMemoryFeature = enumLinks.Next()
                schInMemoryLink = CType(schInMemoryFeature, ISchematicInMemoryFeatureLink)

            End While
            'caculate angle bisector
            If (enableCalculate) Then
                angleBisector = CalculateAngleBisector(angle1, angle2)
            Else
                Continue For
            End If

            'construct a geometry for the new node node
            'now call alterNode to create a new schematic feature
            'construct a correct name
            Dim uniqueNodeName As String
            Dim featureCreateName As String
            featureCreateName = schFeatureParent.Name & Separator & extensionName
            Dim elementType As esriSchematicElementType = esriSchematicElementType.esriSchematicNodeType
            uniqueNodeName = GetUniqueName(inMemoryDiagram, esriSchematicElementType.esriSchematicNodeType, featureCreateName)

            Dim workspace As IWorkspace = Nothing
            Try
                workspace = inMemoryDiagram.SchematicDiagramClass.SchematicDataset.SchematicWorkspace.Workspace
            Catch
            End Try

            Dim datasourceID As Integer = -1

            If (workspace IsNot Nothing) Then
                datasourceID = rulesHelper.FindDataSourceID(workspace, False)
            End If

            If (datasourceID <> -1) Then
                datasourceID = m_diagramClass.SchematicDataset.DefaultSchematicDataSource.ID
            End If

            Dim schFeatureNodeCreate As ISchematicInMemoryFeature = Nothing
            Dim pointBisector As IPoint = Nothing
            pointBisector = GetCoordPointBisector(pointParent, angleBisector, m_distance)

            Try
                schFeatureNodeCreate = rulesHelper.AlterNode(m_targetNodeClass, uniqueNodeName, Nothing, CType(pointBisector, IGeometry), datasourceID, 0)
            Catch ex As System.Exception
                System.Diagnostics.Trace.WriteLine(ex.Message, "Impossible to create a feature Node")
            End Try

            'now construct a unique link name
            Dim linkName As String = schFeatureParent.Name & Separator & uniqueNodeName
            Dim uniqueLinkName As String

            elementType = esriSchematicElementType.esriSchematicLinkType
            uniqueLinkName = GetUniqueName(inMemoryDiagram, elementType, linkName)
            'construct a link

            Dim schFeatureLinkCreate As ISchematicInMemoryFeature = Nothing
            Try
                schFeatureLinkCreate = rulesHelper.AlterLink(m_targetLinkClass, uniqueLinkName, Nothing, Nothing, datasourceID, 0, schFeatureParent.Name, uniqueNodeName, esriFlowDirection.esriFDWithFlow, 0, 0)
            Catch ex As System.Exception
                System.Diagnostics.Trace.WriteLine(ex.Message, "Impossible to create a feature link")
            End Try
        Next

        If colSchfeatureNode.Count > 0 Then
            colSchfeatureNode.Clear()
        End If

        colSchfeatureNode = Nothing
        rulesHelper = Nothing

    End Sub

    Public ReadOnly Property ClassID() As ESRI.ArcGIS.esriSystem.UID Implements ESRI.ArcGIS.Schematic.ISchematicRule.ClassID
        Get
            Dim ruleID As esriSystem.UID = New esriSystem.UID()
            ruleID.Value = PROGID
            Return ruleID
        End Get
    End Property

    Public ReadOnly Property Description1() As String Implements ESRI.ArcGIS.Schematic.ISchematicRule.Description
        Get
            Return m_description
        End Get
    End Property

    Public Property Description() As String
        Get
            Return m_description
        End Get
        Set(ByVal value As String)
            m_description = value
        End Set
    End Property

    Public ReadOnly Property Name() As String Implements ESRI.ArcGIS.Schematic.ISchematicRule.Name
        Get
            Return "Bisector Rule VBNet"
        End Get
    End Property

    Public ReadOnly Property PropertySet() As ESRI.ArcGIS.esriSystem.IPropertySet Implements ESRI.ArcGIS.Schematic.ISchematicRule.PropertySet
        Get
            Dim propSet As esriSystem.IPropertySet = New esriSystem.PropertySet()
            propSet.SetProperty("DESCRIPTION", m_description)
            propSet.SetProperty("PARENTNODECLASS", m_parentNodeClassName)
            propSet.SetProperty("TARGETNODECLASS", m_targetNodeClassName)
            propSet.SetProperty("TARGETLINKCLASS", m_targetLinkClassName)
            propSet.SetProperty("DISTANCE", m_distance)

            Return propSet
        End Get
    End Property

    Public ReadOnly Property SchematicDiagramClass() As ESRI.ArcGIS.Schematic.ISchematicDiagramClass Implements ESRI.ArcGIS.Schematic.ISchematicRule.SchematicDiagramClass
        Get
            Return m_diagramClass
        End Get
    End Property
#End Region

#Region "ISchematicRuleDesign Members"

    Public Sub Detach() Implements ESRI.ArcGIS.Schematic.ISchematicRuleDesign.Detach
        m_diagramClass = Nothing
        m_parentNodeClass = Nothing
        m_targetNodeClass = Nothing
        m_targetLinkClass = Nothing
    End Sub

    Public WriteOnly Property PropertySet1() As ESRI.ArcGIS.esriSystem.IPropertySet Implements ESRI.ArcGIS.Schematic.ISchematicRuleDesign.PropertySet
        Set(ByVal value As ESRI.ArcGIS.esriSystem.IPropertySet)
            m_description = value.GetProperty("DESCRIPTION").ToString()
            m_parentNodeClassName = value.GetProperty("PARENTNODECLASS").ToString()
            m_targetNodeClassName = value.GetProperty("TARGETNODECLASS").ToString()
            m_targetLinkClassName = value.GetProperty("TARGETLINKCLASS").ToString()
            m_distance = CDbl(value.GetProperty("DISTANCE"))
        End Set
    End Property

    Public Property SchematicDiagramClass1() As ESRI.ArcGIS.Schematic.ISchematicDiagramClass Implements ESRI.ArcGIS.Schematic.ISchematicRuleDesign.SchematicDiagramClass
        Get
            Return m_diagramClass
        End Get
        Set(ByVal value As ESRI.ArcGIS.Schematic.ISchematicDiagramClass)
            m_diagramClass = value
        End Set
    End Property
#End Region

#Region "Bisector Members"
    Private Function GetSchematicInMemoryFeatureClass(ByVal inMemoryDiagram As ESRI.ArcGIS.Schematic.ISchematicInMemoryDiagram, ByVal eltClass As ISchematicElementClass) As ISchematicInMemoryFeatureClass
        Dim SchInMemoryFeatureClassContainer As ISchematicInMemoryFeatureClassContainer
        SchInMemoryFeatureClassContainer = CType(inMemoryDiagram, ISchematicInMemoryFeatureClassContainer)
        GetSchematicInMemoryFeatureClass = SchInMemoryFeatureClassContainer.GetSchematicInMemoryFeatureClass(eltClass)
    End Function

    Private Sub AddNodesDegreeTwo(ByVal enumInMemoryFeature As IEnumSchematicInMemoryFeature, ByVal colSchfeatureNode As System.Collections.Generic.Dictionary(Of String, ISchematicInMemoryFeature), ByVal ruleHelper As ISchematicRulesHelper)
        Dim schInMemoryfeature As ISchematicInMemoryFeature

        If (enumInMemoryFeature Is Nothing Or colSchfeatureNode Is Nothing Or ruleHelper Is Nothing) Then Return

        enumInMemoryFeature.Reset()
        schInMemoryfeature = enumInMemoryFeature.Next()
        While (schInMemoryfeature IsNot Nothing)
            If (schInMemoryfeature.Displayed) Then
                Dim enumLinks As IEnumSchematicInMemoryFeature = Nothing
                enumLinks = ruleHelper.GetDisplayedIncidentLinks(CType(schInMemoryfeature, ISchematicInMemoryFeatureNode), esriSchematicEndPointType.esriSchematicOriginOrExtremityNode)

                If (enumLinks IsNot Nothing And enumLinks.Count = 2) Then
                    If (Not colSchfeatureNode.ContainsKey(schInMemoryfeature.Name)) Then colSchfeatureNode.Add(schInMemoryfeature.Name, schInMemoryfeature)
                End If
            End If

            schInMemoryfeature = enumInMemoryFeature.Next()
        End While
    End Sub

    Private Function GetUniqueName(ByVal inMemoryDiagram As ESRI.ArcGIS.Schematic.ISchematicInMemoryDiagram, ByVal elementType As esriSchematicElementType, ByVal featureName As String) As String
        Dim nameUnique As String = featureName
        Dim schInMemoryfeature As ISchematicInMemoryFeature
        Dim index As Integer = 1
        Dim endWhile As Boolean = False
        Do While Not endWhile
            schInMemoryfeature = inMemoryDiagram.GetSchematicInMemoryFeatureByType(elementType, nameUnique)
            If schInMemoryfeature Is Nothing Then
                endWhile = True
            ElseIf (schInMemoryfeature.Displayed) Then
                nameUnique = nameUnique & index.ToString()
                index = index + 1
            Else
                endWhile = True
            End If
        Loop

        GetUniqueName = nameUnique
    End Function

    Private Function CalculateAngle(ByVal pointFrom As IPoint, ByVal pointTo As IPoint) As Double
        Const radToDegre As Double = 180 / Math.PI
        Dim dX, dY As Double
        CalculateAngle = 0
        dX = pointTo.X - pointFrom.X
        dY = pointTo.Y - pointFrom.Y
        If (dX <> 0) Then
            CalculateAngle = Math.Atan(dY / dX) * radToDegre
        Else ' case 2 points are same abcisse
            If (dY < 0) Then
                CalculateAngle = 270
            Else
                CalculateAngle = 90
            End If
        End If

        If (dX < 0) Then
            CalculateAngle = CalculateAngle + 180
        End If

        If (CalculateAngle < 0) Then
            CalculateAngle = CalculateAngle + 360
        End If

    End Function

    Private Function CalculateAngleBisector(ByVal angle1 As Double, ByVal angle2 As Double) As Double
        Dim angleFinal As Double
        If (Math.Abs(angle1 - angle2) > 180) Then
            angleFinal = (angle1 + angle2) / 2 + 180
        Else
            angleFinal = (angle1 + angle2) / 2
        End If
        CalculateAngleBisector = angleFinal
    End Function

    Private Function GetCoordPointBisector(ByVal pointOrigine As IPoint, ByVal degreeBiSectore As Double, ByVal distance As Double) As IPoint
        Dim pointBisector As IPoint = New Point()
        Dim angleBisector, degreeFinal, dX, dY As Double
        Dim casAngle As Integer = 0
        If (degreeBiSectore <= 90) Then
            degreeFinal = degreeBiSectore
            casAngle = 1
        ElseIf (degreeBiSectore <= 180) Then
            'degreeFinal = degreeBiSectore - 90
            degreeFinal = 180 - degreeBiSectore
            casAngle = 2
        ElseIf (degreeBiSectore <= 270) Then
            degreeFinal = degreeBiSectore - 180
            casAngle = 3
        Else
            degreeFinal = 360 - degreeBiSectore
            casAngle = 4
        End If

        angleBisector = Math.PI * degreeFinal / 180.0

        dY = distance * Math.Sin(angleBisector)
        dX = distance * Math.Cos(angleBisector)

        Dim testx As Double = 0
        Dim testy As Double = 0

        Select Case casAngle
            Case 1 ' case which the vector is on the first of the circle
                pointBisector.X = pointOrigine.X + dX
                pointBisector.Y = pointOrigine.Y + dY
            Case 2 ' case which the vector is on the second of the circle
                pointBisector.X = pointOrigine.X - dX
                pointBisector.Y = pointOrigine.Y + dY
            Case 3 ' case which the vector is on the thirst of the circle
                pointBisector.X = pointOrigine.X - dX
                pointBisector.Y = pointOrigine.Y - dY
            Case Else ' case which the vector is on the fourth of the circle
                pointBisector.X = pointOrigine.X + dX
                pointBisector.Y = pointOrigine.Y - dY
        End Select

        GetCoordPointBisector = pointBisector


    End Function

#End Region
End Class