//*************************************************************************
 //  Method: TryCalculateGraphMetrics()
 //
 /// <summary>
 /// Attempts to calculate a set of one or more related metrics.
 /// </summary>
 ///
 /// <param name="graph">
 /// The graph to calculate metrics for.  The graph may contain duplicate
 /// edges and self-loops.
 /// </param>
 ///
 /// <param name="calculateGraphMetricsContext">
 /// Provides access to objects needed for calculating graph metrics.
 /// </param>
 ///
 /// <param name="graphMetricColumns">
 /// Where an array of GraphMetricColumn objects gets stored if true is
 /// returned, one for each related metric calculated by this method.
 /// </param>
 ///
 /// <returns>
 /// true if the graph metrics were calculated or don't need to be
 /// calculated, false if the user wants to cancel.
 /// </returns>
 ///
 /// <remarks>
 /// This method should periodically check BackgroundWorker.<see
 /// cref="BackgroundWorker.CancellationPending" />.  If true, the method
 /// should immediately return false.
 ///
 /// <para>
 /// It should also periodically report progress by calling the
 /// BackgroundWorker.<see
 /// cref="BackgroundWorker.ReportProgress(Int32, Object)" /> method.  The
 /// userState argument must be a <see cref="GraphMetricProgress" /> object.
 /// </para>
 ///
 /// <para>
 /// Calculated metrics for hidden rows are ignored by the caller, because
 /// Excel misbehaves when values are written to hidden cells.
 /// </para>
 ///
 /// </remarks>
 //*************************************************************************
 public abstract Boolean TryCalculateGraphMetrics(
     IGraph graph,
     CalculateGraphMetricsContext calculateGraphMetricsContext,
     out GraphMetricColumn [] graphMetricColumns
     );
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            GraphMetricUserSettings oGraphMetricUserSettings =
                calculateGraphMetricsContext.GraphMetricUserSettings;

            if (!oGraphMetricUserSettings.ShouldCalculateGraphMetrics(
                    GraphMetrics.Degree |
                    GraphMetrics.InDegree |
                    GraphMetrics.OutDegree
                    ))
            {
                return(true);
            }

            IVertexCollection oVertices = graph.Vertices;
            Int32             iVertices = oVertices.Count;

            // The following lists correspond to vertex worksheet columns.

            List <GraphMetricValueWithID> oInDegreeGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oOutDegreeGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oDegreeGraphMetricValues =
                new List <GraphMetricValueWithID>();

            // Create a dictionary to keep track of vertex degrees.  The key is the
            // IVertex.ID and the value is a zero-based index into the above lists.

            Dictionary <Int32, Int32> oVertexIDDictionary =
                new Dictionary <Int32, Int32>();

            // Calculate the degrees for each vertex using the
            // VertexDegreeCalculator class in the Algorithms namespace, which
            // knows nothing about Excel.
            //
            // For simplicity, all degree metrics (in-degree, out-degree, and
            // degree) are calculated regardless of whether the graph is directed
            // or undirected.  After all metrics are calculated,
            // FilterGraphMetricColumns() filters out the metrics that don't apply
            // to the graph, based on its directedness.

            Dictionary <Int32, VertexDegrees> oVertexDegreeDictionary;

            if (!(new Algorithms.VertexDegreeCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker,
                                         out oVertexDegreeDictionary))
            {
                // The user cancelled.

                return(false);
            }

            Int32 iRowID;

            foreach (IVertex oVertex in oVertices)
            {
                if (!TryGetRowID(oVertex, out iRowID))
                {
                    continue;
                }

                VertexDegrees oVertexDegrees;

                if (!oVertexDegreeDictionary.TryGetValue(oVertex.ID,
                                                         out oVertexDegrees))
                {
                    Debug.Assert(false);
                }

                oInDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                                                   iRowID, oVertexDegrees.InDegree));

                oOutDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                                                    iRowID, oVertexDegrees.OutDegree));

                oDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                                                 iRowID, oVertexDegrees.Degree));

                Debug.Assert(oInDegreeGraphMetricValues.Count ==
                             oOutDegreeGraphMetricValues.Count);

                oVertexIDDictionary.Add(oVertex.ID,
                                        oInDegreeGraphMetricValues.Count - 1);
            }

            // Figure out which columns to add.

            graphMetricColumns = FilterGraphMetricColumns(graph,
                                                          calculateGraphMetricsContext, oInDegreeGraphMetricValues,
                                                          oOutDegreeGraphMetricValues, oDegreeGraphMetricValues);

            return(true);
        }
Beispiel #3
0
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            Object oGroupInformationAsObject;

            // Attempt to retrieve the group information the WorkbookReader object
            // may have stored as metadata on the graph.

            if (
                !calculateGraphMetricsContext.GraphMetricUserSettings.
                ShouldCalculateGraphMetrics(GraphMetrics.GroupMetrics)
                ||
                !graph.TryGetValue(ReservedMetadataKeys.GroupInformation,
                                   typeof(ICollection <GroupInformation>),
                                   out oGroupInformationAsObject)
                )
            {
                return(true);
            }

            // The following lists correspond to group worksheet columns.

            List <GraphMetricValueWithID> oVerticesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oUniqueEdgesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oEdgesWithDuplicatesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oTotalEdgesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oSelfLoopsGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oConnectedComponentsGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID>
            oSingleVertexConnectedComponentsGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID>
            oMaximumConnectedComponentVerticesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID>
            oMaximumConnectedComponentEdgesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oMaximumGeodesicDistanceGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oAverageGeodesicDistanceGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oGraphDensityGraphMetricValues =
                new List <GraphMetricValueWithID>();

            foreach (GroupInformation oGroupInformation in
                     (ICollection <GroupInformation>)oGroupInformationAsObject)
            {
                ICollection <IVertex> oVertices = oGroupInformation.Vertices;

                if (oVertices == null || oVertices.Count == 0 ||
                    !oGroupInformation.RowID.HasValue)
                {
                    continue;
                }

                OverallMetrics oOverallMetrics;

                if (!TryCalculateGraphMetricsForOneGroup(oGroupInformation,
                                                         calculateGraphMetricsContext, out oOverallMetrics))
                {
                    // The user cancelled.

                    return(false);
                }

                Int32 iRowID = oGroupInformation.RowID.Value;

                oVerticesGraphMetricValues.Add(new GraphMetricValueWithID(
                                                   iRowID, oOverallMetrics.Vertices));

                oUniqueEdgesGraphMetricValues.Add(new GraphMetricValueWithID(
                                                      iRowID, oOverallMetrics.UniqueEdges));

                oEdgesWithDuplicatesGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.EdgesWithDuplicates));

                oTotalEdgesGraphMetricValues.Add(new GraphMetricValueWithID(iRowID,
                                                                            oOverallMetrics.TotalEdges));

                oSelfLoopsGraphMetricValues.Add(new GraphMetricValueWithID(iRowID,
                                                                           oOverallMetrics.SelfLoops));

                oConnectedComponentsGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.ConnectedComponents));

                oSingleVertexConnectedComponentsGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.SingleVertexConnectedComponents));

                oMaximumConnectedComponentVerticesGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.MaximumConnectedComponentVertices));

                oMaximumConnectedComponentEdgesGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.MaximumConnectedComponentEdges));

                oMaximumGeodesicDistanceGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               OverallMetricCalculator2.NullableToGraphMetricValue <Int32>(
                                                   oOverallMetrics.MaximumGeodesicDistance)));

                oAverageGeodesicDistanceGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               OverallMetricCalculator2.NullableToGraphMetricValue <Double>(
                                                   oOverallMetrics.AverageGeodesicDistance)));

                oGraphDensityGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               OverallMetricCalculator2.NullableToGraphMetricValue <Double>(
                                                   oOverallMetrics.GraphDensity)));
            }

            graphMetricColumns = new GraphMetricColumn [] {
                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups, GroupTableColumnNames.Vertices,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oVerticesGraphMetricValues.ToArray()),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups, GroupTableColumnNames.UniqueEdges,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oUniqueEdgesGraphMetricValues.ToArray()),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups, GroupTableColumnNames.EdgesWithDuplicates,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oEdgesWithDuplicatesGraphMetricValues.ToArray()),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups, GroupTableColumnNames.TotalEdges,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oTotalEdgesGraphMetricValues.ToArray()),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups, GroupTableColumnNames.SelfLoops,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oSelfLoopsGraphMetricValues.ToArray()),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups, GroupTableColumnNames.ConnectedComponents,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oConnectedComponentsGraphMetricValues.ToArray()),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups,
                                            GroupTableColumnNames.SingleVertexConnectedComponents,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oSingleVertexConnectedComponentsGraphMetricValues.ToArray()),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups,
                                            GroupTableColumnNames.MaximumConnectedComponentVertices,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oMaximumConnectedComponentVerticesGraphMetricValues.ToArray()
                                            ),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups,
                                            GroupTableColumnNames.MaximumConnectedComponentEdges,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oMaximumConnectedComponentEdgesGraphMetricValues.ToArray()
                                            ),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups,
                                            GroupTableColumnNames.MaximumGeodesicDistance,
                                            ExcelUtil.AutoColumnWidth, Int32NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oMaximumGeodesicDistanceGraphMetricValues.ToArray()),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups,
                                            GroupTableColumnNames.AverageGeodesicDistance,
                                            ExcelUtil.AutoColumnWidth, DoubleNumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oAverageGeodesicDistanceGraphMetricValues.ToArray()),

                new GraphMetricColumnWithID(WorksheetNames.Groups,
                                            TableNames.Groups, GroupTableColumnNames.GraphDensity,
                                            ExcelUtil.AutoColumnWidth, DoubleNumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oGraphDensityGraphMetricValues.ToArray()),
            };

            return(true);
        }
        //*************************************************************************
        //  Method: TryCalculateGraphMetrics()
        //
        /// <summary>
        /// Attempts to calculate a set of one or more related metrics.
        /// </summary>
        ///
        /// <param name="graph">
        /// The graph to calculate metrics for.  The graph may contain duplicate
        /// edges and self-loops.
        /// </param>
        ///
        /// <param name="calculateGraphMetricsContext">
        /// Provides access to objects needed for calculating graph metrics.
        /// </param>
        ///
        /// <param name="graphMetricColumns">
        /// Where an array of GraphMetricColumn objects gets stored if true is
        /// returned, one for each related metric calculated by this method.
        /// </param>
        ///
        /// <returns>
        /// true if the graph metrics were calculated or don't need to be
        /// calculated, false if the user wants to cancel.
        /// </returns>
        ///
        /// <remarks>
        /// This method periodically checks BackgroundWorker.<see
        /// cref="BackgroundWorker.CancellationPending" />.  If true, the method
        /// immediately returns false.
        ///
        /// <para>
        /// It also periodically reports progress by calling the
        /// BackgroundWorker.<see
        /// cref="BackgroundWorker.ReportProgress(Int32, Object)" /> method.  The
        /// userState argument is a <see cref="GraphMetricProgress" /> object.
        /// </para>
        ///
        /// <para>
        /// Calculated metrics for hidden rows are ignored by the caller, because
        /// Excel misbehaves when values are written to hidden cells.
        /// </para>
        ///
        /// </remarks>
        //*************************************************************************
        public override Boolean TryCalculateGraphMetrics(
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
            )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            return ( TryCalculateGraphMetrics(graph, calculateGraphMetricsContext,
            new Algorithms.EigenvectorCentralityCalculator(),

            calculateGraphMetricsContext.GraphMetricUserSettings.
                ShouldCalculateGraphMetrics(GraphMetrics.EigenvectorCentrality),

            VertexTableColumnNames.EigenvectorCentrality,
            VertexTableColumnWidths.EigenvectorCentrality,
            CellStyleNames.GraphMetricGood, out graphMetricColumns) );
        }
        //*************************************************************************
        //  Method: TryCalculateGraphMetrics()
        //
        /// <summary>
        /// Attempts to calculate a set of one or more related metrics.
        /// </summary>
        ///
        /// <param name="graph">
        /// The graph to calculate metrics for.  The graph may contain duplicate
        /// edges and self-loops.
        /// </param>
        ///
        /// <param name="calculateGraphMetricsContext">
        /// Provides access to objects needed for calculating graph metrics.
        /// </param>
        ///
        /// <param name="graphMetricColumns">
        /// Where an array of GraphMetricColumn objects gets stored if true is
        /// returned, one for each related metric calculated by this method.
        /// </param>
        ///
        /// <returns>
        /// true if the graph metrics were calculated, false if the user wants to
        /// cancel.
        /// </returns>
        ///
        /// <remarks>
        /// This method periodically checks BackgroundWorker.<see
        /// cref="BackgroundWorker.CancellationPending" />.  If true, the method
        /// immediately returns false.
        ///
        /// <para>
        /// It also periodically reports progress by calling the
        /// BackgroundWorker.<see
        /// cref="BackgroundWorker.ReportProgress(Int32, Object)" /> method.  The
        /// userState argument is a <see cref="GraphMetricProgress" /> object.
        /// </para>
        ///
        /// <para>
        /// Calculated metrics for hidden rows are ignored by the caller, because
        /// Excel misbehaves when values are written to hidden cells.
        /// </para>
        ///
        /// </remarks>
        //*************************************************************************
        public override Boolean TryCalculateGraphMetrics(
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
            )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if ( !calculateGraphMetricsContext.GraphMetricUserSettings.
            CalculateGraphMetrics(GraphMetrics.OverallMetrics) )
            {
            return (true);
            }

            // Calculate the overall metrics using the OverallMetricCalculator
            // class in the Algorithms namespace, which knows nothing about Excel.

            OverallMetrics oOverallMetrics;

            if ( !( new Algorithms.OverallMetricCalculator() ).
            TryCalculateGraphMetrics(graph,
                calculateGraphMetricsContext.BackgroundWorker,
                out oOverallMetrics) )
            {
            // The user cancelled.

            return (false);
            }

            OverallMetricRows oOverallMetricRows = new OverallMetricRows();

            //*********************************
            // Graph type
            //*********************************

            AddRow("Graph Type", oOverallMetrics.Directedness.ToString(),
            oOverallMetricRows);

            //*********************************
            // Vertex count
            //*********************************

            AddRow(oOverallMetricRows);
            AddRow("Vertices", oOverallMetrics.Vertices, oOverallMetricRows);

            //*********************************
            // Edge counts
            //*********************************

            String sDuplicateEdgeStyle = CellStyleNames.GraphMetricGood;
            String sDuplicateEdgeComments = String.Empty;
            String sGraphDensityComments = String.Empty;

            if (oOverallMetrics.EdgesWithDuplicates > 0)
            {
            // The graph density is rendered invalid when the graph has
            // duplicate edges.

            sDuplicateEdgeStyle = CellStyleNames.GraphMetricBad;

            sDuplicateEdgeComments =
                "You can merge duplicate edges using NodeXL, Data, Prepare"
                + " Data, Merge Duplicate Edges."
                ;

            sGraphDensityComments =
                "The workbook contains duplicate edges that have caused the"
                + " graph density to be inaccurate.  "
                + sDuplicateEdgeComments
                ;
            }

            AddRow(oOverallMetricRows);
            AddRow("Unique Edges", oOverallMetrics.UniqueEdges, oOverallMetricRows);

            AddRow("Edges With Duplicates",
            FormatInt32(oOverallMetrics.EdgesWithDuplicates),
            sDuplicateEdgeComments, sDuplicateEdgeStyle,
            oOverallMetricRows);

            AddRow("Total Edges", oOverallMetrics.TotalEdges, oOverallMetricRows);

            //*********************************
            // Self-loops
            //*********************************

            AddRow(oOverallMetricRows);
            AddRow("Self-Loops", oOverallMetrics.SelfLoops, oOverallMetricRows);

            //*********************************
            // Connected component counts
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("Connected Components", oOverallMetrics.ConnectedComponents,
            oOverallMetricRows);

            AddRow("Single-Vertex Connected Components",
            oOverallMetrics.SingleVertexConnectedComponents,
            oOverallMetricRows);

            AddRow("Maximum Vertices in a Connected Component",
            oOverallMetrics.MaximumConnectedComponentVertices,
            oOverallMetricRows);

            AddRow("Maximum Edges in a Connected Component",
            oOverallMetrics.MaximumConnectedComponentEdges,
            oOverallMetricRows);

            //*********************************
            // Geodesic distances
            //*********************************

            String sMaximumGeodesicDistance, sAverageGeodesicDistance;

            GetGeodesicDistanceStrings(oOverallMetrics,
            out sMaximumGeodesicDistance, out sAverageGeodesicDistance);

            AddRow(oOverallMetricRows);

            AddRow("Maximum Geodesic Distance (Diameter)",
            sMaximumGeodesicDistance, oOverallMetricRows);

            AddRow("Average Geodesic Distance", sAverageGeodesicDistance,
            oOverallMetricRows);

            //*********************************
            // Graph density
            //*********************************

            Nullable<Double> dGraphDensity = oOverallMetrics.GraphDensity;

            AddRow(oOverallMetricRows);

            AddRow("Graph Density", dGraphDensity.HasValue ?
            FormatDouble(dGraphDensity.Value) : NotApplicableMessage,
            sGraphDensityComments, sDuplicateEdgeStyle, oOverallMetricRows);

            //*********************************
            // NodeXL version
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("NodeXL Version", AssemblyUtil2.GetFileVersion(),
            oOverallMetricRows);

            graphMetricColumns = new GraphMetricColumn[] {

            CreateGraphMetricColumnOrdered(
                OverallMetricsTableColumnNames.Name,
                oOverallMetricRows.MetricNames),

            CreateGraphMetricColumnOrdered(
                OverallMetricsTableColumnNames.Value,
                oOverallMetricRows.MetricValues),

            CreateGraphMetricColumnOrdered(
                OverallMetricsTableColumnNames.Comments,
                oOverallMetricRows.MetricComments),
            };

            return (true);
        }
Beispiel #6
0
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            IGraphMetricCalculator graphMetricCalculator,
            Boolean calculateGraphMetric,
            String columnName,
            Double columnWidthChars,
            String style,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            Debug.Assert(graphMetricCalculator != null);
            Debug.Assert(!String.IsNullOrEmpty(columnName));

            Debug.Assert(columnWidthChars == ExcelUtil.AutoColumnWidth ||
                         columnWidthChars > 0);

            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if (!calculateGraphMetric)
            {
                return(true);
            }

            // Calculate the graph metrics for each vertex using the
            // IGraphMetricCalculator object, which knows nothing about Excel.

            Object oGraphMetricsAsObject;

            if (!graphMetricCalculator.TryCalculateGraphMetrics(graph,
                                                                calculateGraphMetricsContext.BackgroundWorker,
                                                                out oGraphMetricsAsObject))
            {
                // The user cancelled.

                return(false);
            }

            Debug.Assert(oGraphMetricsAsObject is Dictionary <Int32, Double>);

            Dictionary <Int32, Double> oGraphMetrics =
                (Dictionary <Int32, Double>)oGraphMetricsAsObject;

            // Transfer the graph metrics to an array of GraphMetricValue objects.

            List <GraphMetricValueWithID> oGraphMetricValues =
                new List <GraphMetricValueWithID>();

            foreach (IVertex oVertex in graph.Vertices)
            {
                // Try to get the row ID stored in the worksheet.

                Int32 iRowID;

                if (TryGetRowID(oVertex, out iRowID))
                {
                    oGraphMetricValues.Add(new GraphMetricValueWithID(
                                               iRowID, oGraphMetrics[oVertex.ID]));
                }
            }

            graphMetricColumns = new GraphMetricColumn [] {
                new GraphMetricColumnWithID(WorksheetNames.Vertices,
                                            TableNames.Vertices, columnName, columnWidthChars,
                                            NumericFormat, style, oGraphMetricValues.ToArray()
                                            )
            };

            return(true);
        }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if (!calculateGraphMetricsContext.GraphMetricUserSettings.
                CalculateGraphMetrics(GraphMetrics.OverallMetrics))
            {
                return(true);
            }

            // Calculate the overall metrics using the OverallMetricCalculator
            // class in the Algorithms namespace, which knows nothing about Excel.

            OverallMetrics oOverallMetrics;

            if (!(new Algorithms.OverallMetricCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker,
                                         out oOverallMetrics))
            {
                // The user cancelled.

                return(false);
            }

            OverallMetricRows oOverallMetricRows = new OverallMetricRows();


            //*********************************
            // Graph type
            //*********************************

            AddRow("Graph Type", oOverallMetrics.Directedness.ToString(),
                   oOverallMetricRows);


            //*********************************
            // Vertex count
            //*********************************

            AddRow(oOverallMetricRows);
            AddRow("Vertices", oOverallMetrics.Vertices, oOverallMetricRows);


            //*********************************
            // Edge counts
            //*********************************

            String sDuplicateEdgeStyle    = CellStyleNames.GraphMetricGood;
            String sDuplicateEdgeComments = String.Empty;
            String sGraphDensityComments  = String.Empty;

            if (oOverallMetrics.EdgesWithDuplicates > 0)
            {
                // The graph density is rendered invalid when the graph has
                // duplicate edges.

                sDuplicateEdgeStyle = CellStyleNames.GraphMetricBad;

                sDuplicateEdgeComments =
                    "You can merge duplicate edges using NodeXL, Data, Prepare"
                    + " Data, Merge Duplicate Edges."
                ;

                sGraphDensityComments =
                    "The workbook contains duplicate edges that have caused the"
                    + " graph density to be inaccurate.  "
                    + sDuplicateEdgeComments
                ;
            }

            AddRow(oOverallMetricRows);
            AddRow("Unique Edges", oOverallMetrics.UniqueEdges, oOverallMetricRows);

            AddRow("Edges With Duplicates",
                   FormatInt32(oOverallMetrics.EdgesWithDuplicates),
                   sDuplicateEdgeComments, sDuplicateEdgeStyle,
                   oOverallMetricRows);

            AddRow("Total Edges", oOverallMetrics.TotalEdges, oOverallMetricRows);


            //*********************************
            // Self-loops
            //*********************************

            AddRow(oOverallMetricRows);
            AddRow("Self-Loops", oOverallMetrics.SelfLoops, oOverallMetricRows);


            //*********************************
            // Connected component counts
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("Connected Components", oOverallMetrics.ConnectedComponents,
                   oOverallMetricRows);

            AddRow("Single-Vertex Connected Components",
                   oOverallMetrics.SingleVertexConnectedComponents,
                   oOverallMetricRows);

            AddRow("Maximum Vertices in a Connected Component",
                   oOverallMetrics.MaximumConnectedComponentVertices,
                   oOverallMetricRows);

            AddRow("Maximum Edges in a Connected Component",
                   oOverallMetrics.MaximumConnectedComponentEdges,
                   oOverallMetricRows);


            //*********************************
            // Geodesic distances
            //*********************************

            String sMaximumGeodesicDistance, sAverageGeodesicDistance;

            GetGeodesicDistanceStrings(oOverallMetrics,
                                       out sMaximumGeodesicDistance, out sAverageGeodesicDistance);

            AddRow(oOverallMetricRows);

            AddRow("Maximum Geodesic Distance (Diameter)",
                   sMaximumGeodesicDistance, oOverallMetricRows);

            AddRow("Average Geodesic Distance", sAverageGeodesicDistance,
                   oOverallMetricRows);


            //*********************************
            // Graph density
            //*********************************

            Nullable <Double> dGraphDensity = oOverallMetrics.GraphDensity;

            AddRow(oOverallMetricRows);

            AddRow("Graph Density", dGraphDensity.HasValue ?
                   FormatDouble(dGraphDensity.Value) : NotApplicableMessage,
                   sGraphDensityComments, sDuplicateEdgeStyle, oOverallMetricRows);


            //*********************************
            // NodeXL version
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("NodeXL Version", AssemblyUtil2.GetFileVersion(),
                   oOverallMetricRows);


            graphMetricColumns = new GraphMetricColumn[] {
                CreateGraphMetricColumnOrdered(
                    OverallMetricsTableColumnNames.Name,
                    oOverallMetricRows.MetricNames),

                CreateGraphMetricColumnOrdered(
                    OverallMetricsTableColumnNames.Value,
                    oOverallMetricRows.MetricValues),

                CreateGraphMetricColumnOrdered(
                    OverallMetricsTableColumnNames.Comments,
                    oOverallMetricRows.MetricComments),
            };

            return(true);
        }
Beispiel #8
0
        TryGetRequiredColumnInformation
        (
            GraphMetricColumn oGraphMetricColumn,
            ListObject oTable,
            out Range oVisibleColumnData
        )
        {
            Debug.Assert(oGraphMetricColumn != null);
            Debug.Assert(oTable != null);
            AssertValid();

            oVisibleColumnData = null;

            // Add the specified column if it's not already present.

            String sColumnName  = oGraphMetricColumn.ColumnName;
            String sColumnStyle = oGraphMetricColumn.Style;

            Microsoft.Office.Interop.Excel.ListColumn oColumn;

            if (
                !ExcelUtil.TryGetTableColumn(oTable, sColumnName, out oColumn)
                &&
                !ExcelUtil.TryAddTableColumn(oTable, sColumnName,
                                             oGraphMetricColumn.ColumnWidthChars, sColumnStyle, out oColumn)
                )
            {
                // Give up.

                return(false);
            }

            Range oColumnData;

            if (!ExcelUtil.TryGetTableColumnData(oTable, sColumnName,
                                                 out oColumnData))
            {
                return(false);
            }

            ExcelUtil.SetRangeStyle(oColumnData, sColumnStyle);

            String sNumberFormat = oGraphMetricColumn.NumberFormat;

            if (sNumberFormat != null)
            {
                oColumnData.NumberFormat = sNumberFormat;
            }

            // Wrapping text makes Range.set_Value() very slow, so turn it off.

            oColumn.Range.WrapText = false;

            // But wrap the text in the column's header.

            ((Range)oColumn.Range.Cells[1, 1]).WrapText = true;

            // Get the visible range.

            if (!ExcelUtil.TryGetVisibleRange(oColumnData,
                                              out oVisibleColumnData))
            {
                return(false);
            }

            return(true);
        }
        //*************************************************************************
        //  Method: TryCalculateGraphMetrics()
        //
        /// <summary>
        /// Attempts to calculate a set of one or more related metrics.
        /// </summary>
        ///
        /// <param name="graph">
        /// The graph to calculate metrics for.  The graph may contain duplicate
        /// edges and self-loops.
        /// </param>
        ///
        /// <param name="calculateGraphMetricsContext">
        /// Provides access to objects needed for calculating graph metrics.
        /// </param>
        ///
        /// <param name="oneDoubleGraphMetricCalculator">
        /// Graph metric calculator in the Algorithms namespace that calculates
        /// one graph metric of type Double.
        /// </param>
        ///
        /// <param name="calculateGraphMetric">
        /// true to calculate the graph metric, false to skip it.
        /// </param>
        ///
        /// <param name="columnName">
        /// Name of the column to write to in the vertex table.
        /// </param>
        ///
        /// <param name="columnWidthChars">
        /// Width of the column, in characters, or <see
        /// cref="Microsoft.Research.CommunityTechnologies.AppLib.ExcelUtil.
        /// AutoColumnWidth" /> to set the width automatically.
        /// </param>
        ///
        /// <param name="style">
        /// Style of the column, or null to apply Excel's normal style.  Sample:
        /// "Bad".
        /// </param>
        ///
        /// <param name="graphMetricColumns">
        /// Where an array of GraphMetricColumn objects gets stored if true is
        /// returned, one for each related metric calculated by this method.
        /// </param>
        ///
        /// <returns>
        /// true if the graph metrics were calculated or don't need to be
        /// calculated, false if the user wants to cancel.
        /// </returns>
        ///
        /// <remarks>
        /// This method periodically checks BackgroundWorker.<see
        /// cref="BackgroundWorker.CancellationPending" />.  If true, the method
        /// immediately returns false.
        ///
        /// <para>
        /// It also periodically reports progress by calling the
        /// BackgroundWorker.<see
        /// cref="BackgroundWorker.ReportProgress(Int32, Object)" /> method.  The
        /// userState argument is a <see cref="GraphMetricProgress" /> object.
        /// </para>
        ///
        /// <para>
        /// Calculated metrics for hidden rows are ignored by the caller, because
        /// Excel misbehaves when values are written to hidden cells.
        /// </para>
        ///
        /// </remarks>
        //*************************************************************************
        protected Boolean TryCalculateGraphMetrics(
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            
            Algorithms.OneDoubleGraphMetricCalculatorBase
            oneDoubleGraphMetricCalculator,
            
            Boolean calculateGraphMetric,
            String columnName,
            Double columnWidthChars,
            String style,
            out GraphMetricColumn [] graphMetricColumns
            )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            Debug.Assert(oneDoubleGraphMetricCalculator != null);
            Debug.Assert( !String.IsNullOrEmpty(columnName) );

            Debug.Assert(columnWidthChars == ExcelUtil.AutoColumnWidth ||
            columnWidthChars > 0);

            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if (!calculateGraphMetric)
            {
            return (true);
            }

            // Calculate the graph metrics for each vertex using the
            // OneDoubleGraphMetricCalculatorBase object, which knows nothing about
            // Excel.

            Dictionary<Int32, Double> oGraphMetrics;

            if ( !oneDoubleGraphMetricCalculator.TryCalculateGraphMetrics(graph,
            calculateGraphMetricsContext.BackgroundWorker, out oGraphMetrics) )
            {
            // The user cancelled.

            return (false);
            }

            // Transfer the graph metrics to an array of GraphMetricValue objects.

            List<GraphMetricValueWithID> oGraphMetricValues =
            new List<GraphMetricValueWithID>();

            foreach (IVertex oVertex in graph.Vertices)
            {
            // Try to get the row ID stored in the worksheet.

            Int32 iRowID;

            if ( TryGetRowID(oVertex, out iRowID) )
            {
                oGraphMetricValues.Add( new GraphMetricValueWithID(
                    iRowID, oGraphMetrics[oVertex.ID] ) );
            }
            }

            graphMetricColumns = new GraphMetricColumn [] {
            new GraphMetricColumnWithID( WorksheetNames.Vertices,
                TableNames.Vertices, columnName, columnWidthChars,
                NumericFormat, style, oGraphMetricValues.ToArray()
                ) };

            return (true);
        }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if (!calculateGraphMetricsContext.GraphMetricUserSettings.
                ShouldCalculateGraphMetrics(GraphMetrics.BrandesFastCentralities))
            {
                return(true);
            }

            // Calculate the centralities for each vertex using the
            // BrandesFastCalculator class in the Algorithms namespace, which knows
            // nothing about Excel.

            Dictionary <Int32, Algorithms.BrandesVertexCentralities>
            oVertexCentralitiesDictionary;

            if (!(new Algorithms.BrandesFastCentralityCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker,
                                         out oVertexCentralitiesDictionary))
            {
                // The user cancelled.

                return(false);
            }

            // Transfer the centralities to arrays of GraphMetricValue objects.

            List <GraphMetricValueWithID> oBetweennessCentralityValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oClosenessCentralityValues =
                new List <GraphMetricValueWithID>();

            foreach (IVertex oVertex in graph.Vertices)
            {
                // Try to get the row ID stored in the worksheet.

                Int32 iRowID;

                if (TryGetRowID(oVertex, out iRowID))
                {
                    Algorithms.BrandesVertexCentralities oVertexCentralities =
                        oVertexCentralitiesDictionary[oVertex.ID];

                    oBetweennessCentralityValues.Add(
                        new GraphMetricValueWithID(iRowID,
                                                   oVertexCentralities.BetweennessCentrality
                                                   ));

                    oClosenessCentralityValues.Add(
                        new GraphMetricValueWithID(iRowID,
                                                   oVertexCentralities.ClosenessCentrality
                                                   ));
                }
            }

            graphMetricColumns = new GraphMetricColumn [] {
                new GraphMetricColumnWithID(WorksheetNames.Vertices,
                                            TableNames.Vertices,
                                            VertexTableColumnNames.BetweennessCentrality,
                                            VertexTableColumnWidths.BetweennessCentrality,
                                            NumericFormat, CellStyleNames.GraphMetricGood,
                                            oBetweennessCentralityValues.ToArray()
                                            ),

                new GraphMetricColumnWithID(WorksheetNames.Vertices,
                                            TableNames.Vertices,
                                            VertexTableColumnNames.ClosenessCentrality,
                                            VertexTableColumnWidths.ClosenessCentrality,
                                            NumericFormat, CellStyleNames.GraphMetricGood,
                                            oClosenessCentralityValues.ToArray()
                                            )
            };

            return(true);
        }
        //*************************************************************************
        //  Method: WriteGraphMetricColumnsToWorkbook()
        //
        /// <summary>
        /// Writes an array of GraphMetricColumn objects to the workbook.
        /// </summary>
        ///
        /// <param name="aoGraphMetricColumns">
        /// An array of GraphMetricColumn objects, one for each column of metrics
        /// that were calculated.
        /// </param>
        //*************************************************************************
        protected void WriteGraphMetricColumnsToWorkbook(
            GraphMetricColumn [] aoGraphMetricColumns
            )
        {
            Debug.Assert(aoGraphMetricColumns != null);
            AssertValid();

            Microsoft.Office.Interop.Excel.Application oApplication =
            m_oWorkbook.Application;

            GraphMetricWriter oGraphMetricWriter = new GraphMetricWriter();

            oApplication.ScreenUpdating = false;

            try
            {
            oGraphMetricWriter.WriteGraphMetricColumnsToWorkbook(
                aoGraphMetricColumns, m_oWorkbook);

            // Let the user know that graph metrics have been calculated.

            oGraphMetricWriter.ActivateRelevantWorksheet(
                aoGraphMetricColumns, m_oWorkbook);
            }
            catch (Exception oException)
            {
            oApplication.ScreenUpdating = true;

            ErrorUtil.OnException(oException);

            this.Close();
            return;
            }

            oApplication.ScreenUpdating = true;
        }
        //*************************************************************************
        //  Method: TryCalculateGraphMetrics()
        //
        /// <summary>
        /// Attempts to calculate a set of one or more related metrics.
        /// </summary>
        ///
        /// <param name="graph">
        /// The graph to calculate metrics for.  The graph may contain duplicate
        /// edges and self-loops.
        /// </param>
        ///
        /// <param name="calculateGraphMetricsContext">
        /// Provides access to objects needed for calculating graph metrics.
        /// </param>
        ///
        /// <param name="graphMetricColumns">
        /// Where an array of GraphMetricColumn objects gets stored if true is
        /// returned, one for each related metric calculated by this method.
        /// </param>
        ///
        /// <returns>
        /// true if the graph metrics were calculated or don't need to be
        /// calculated, false if the user wants to cancel.
        /// </returns>
        ///
        /// <remarks>
        /// This method periodically checks BackgroundWorker.<see
        /// cref="BackgroundWorker.CancellationPending" />.  If true, the method
        /// immediately returns false.
        ///
        /// <para>
        /// It also periodically reports progress by calling the
        /// BackgroundWorker.<see
        /// cref="BackgroundWorker.ReportProgress(Int32, Object)" /> method.  The
        /// userState argument is a <see cref="GraphMetricProgress" /> object.
        /// </para>
        ///
        /// <para>
        /// Calculated metrics for hidden rows are ignored by the caller, because
        /// Excel misbehaves when values are written to hidden cells.
        /// </para>
        ///
        /// </remarks>
        //*************************************************************************
        public override Boolean TryCalculateGraphMetrics(
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
            )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if ( !calculateGraphMetricsContext.GraphMetricUserSettings.
            ShouldCalculateGraphMetrics(GraphMetrics.BrandesFastCentralities) )
            {
            return (true);
            }

            // Calculate the centralities for each vertex using the
            // BrandesFastCalculator class in the Algorithms namespace, which knows
            // nothing about Excel.

            Dictionary<Int32, Algorithms.BrandesVertexCentralities>
            oVertexCentralitiesDictionary;

            if ( !( new Algorithms.BrandesFastCentralityCalculator() ).
            TryCalculateGraphMetrics(graph,
                calculateGraphMetricsContext.BackgroundWorker,
                out oVertexCentralitiesDictionary) )
            {
            // The user cancelled.

            return (false);
            }

            // Transfer the centralities to arrays of GraphMetricValue objects.

            List<GraphMetricValueWithID> oBetweennessCentralityValues =
            new List<GraphMetricValueWithID>();

            List<GraphMetricValueWithID> oClosenessCentralityValues =
            new List<GraphMetricValueWithID>();

            foreach (IVertex oVertex in graph.Vertices)
            {
            // Try to get the row ID stored in the worksheet.

            Int32 iRowID;

            if ( TryGetRowID(oVertex, out iRowID) )
            {
                Algorithms.BrandesVertexCentralities oVertexCentralities =
                    oVertexCentralitiesDictionary[oVertex.ID];

                oBetweennessCentralityValues.Add(
                    new GraphMetricValueWithID(iRowID,
                        oVertexCentralities.BetweennessCentrality
                    ) );

                oClosenessCentralityValues.Add(
                    new GraphMetricValueWithID(iRowID,
                        oVertexCentralities.ClosenessCentrality
                    ) );
            }
            }

            graphMetricColumns = new GraphMetricColumn [] {

            new GraphMetricColumnWithID( WorksheetNames.Vertices,
                TableNames.Vertices,
                VertexTableColumnNames.BetweennessCentrality,
                VertexTableColumnWidths.BetweennessCentrality,
                NumericFormat, CellStyleNames.GraphMetricGood,
                oBetweennessCentralityValues.ToArray()
                ),

            new GraphMetricColumnWithID( WorksheetNames.Vertices,
                TableNames.Vertices,
                VertexTableColumnNames.ClosenessCentrality,
                VertexTableColumnWidths.ClosenessCentrality,
                NumericFormat, CellStyleNames.GraphMetricGood,
                oClosenessCentralityValues.ToArray()
                )
                };

            return (true);
        }
        //*************************************************************************
        //  Method: TryGetRequiredColumnInformation()
        //
        /// <summary>
        /// Gets the column information required to write a GraphMetricColumn
        /// object to the workbook.
        /// </summary>
        ///
        /// <param name="oGraphMetricColumn">
        /// The GraphMetricColumn object to write to the workbook.
        /// </param>
        ///
        /// <param name="oTable">
        /// The table containing the column.
        /// </param>
        ///
        /// <param name="oVisibleColumnData">
        /// Where the visible range of the specified column gets stored if true is
        /// returned.
        /// </param>
        ///
        /// <returns>
        /// true if the column information was obtained.
        /// </returns>
        //*************************************************************************
        protected Boolean TryGetRequiredColumnInformation(
            GraphMetricColumn oGraphMetricColumn,
            ListObject oTable,
            out Range oVisibleColumnData
            )
        {
            Debug.Assert(oGraphMetricColumn != null);
            Debug.Assert(oTable != null);
            AssertValid();

            oVisibleColumnData = null;

            // Add the specified column if it's not already present.

            String sColumnName = oGraphMetricColumn.ColumnName;
            String sColumnStyle = oGraphMetricColumn.Style;

            Microsoft.Office.Interop.Excel.ListColumn oColumn;

            if (
            !ExcelUtil.TryGetTableColumn(oTable, sColumnName, out oColumn)
            &&
            !ExcelUtil.TryAddTableColumn(oTable, sColumnName,
                oGraphMetricColumn.ColumnWidthChars, sColumnStyle, out oColumn)
            )
            {
            // Give up.

            return (false);
            }

            Range oColumnData;

            if ( !ExcelUtil.TryGetTableColumnData(oTable, sColumnName,
            out oColumnData) )
            {
            return (false);
            }

            ExcelUtil.SetRangeStyle(oColumnData, sColumnStyle);

            String sNumberFormat = oGraphMetricColumn.NumberFormat;

            if (sNumberFormat != null)
            {
            oColumnData.NumberFormat = sNumberFormat;
            }

            // Wrapping text makes Range.set_Value() very slow, so turn it off.

            oColumn.Range.WrapText = false;

            // But wrap the text in the column's header.

            ( (Range)oColumn.Range.Cells[1, 1] ).WrapText = true;

            // Get the visible range.

            if ( !ExcelUtil.TryGetVisibleRange(oColumnData,
            out oVisibleColumnData) )
            {
            return (false);
            }

            return (true);
        }
        //*************************************************************************
        //  Method: WriteGraphMetricColumnsToWorkbook()
        //
        /// <summary>
        /// Writes an array of GraphMetricColumn objects to the workbook.
        /// </summary>
        ///
        /// <param name="graphMetricColumns">
        /// An array of GraphMetricColumn objects, one for each column of metrics
        /// that should be written to the workbook.
        /// </param>
        ///
        /// <param name="workbook">
        /// Workbook containing the graph contents.
        /// </param>
        //*************************************************************************
        public void WriteGraphMetricColumnsToWorkbook(
            GraphMetricColumn [] graphMetricColumns,
            Microsoft.Office.Interop.Excel.Workbook workbook
            )
        {
            Debug.Assert(graphMetricColumns != null);
            Debug.Assert(workbook != null);
            AssertValid();

            // (Note: Don't sort grapMetricColumns by worksheet name/table name in
            // an effort to minimize worksheet switches in the code below.  That
            // would interfere with the column order specified by the
            // IGraphMetricCalculator2 implementations.

            // Create a dictionary of tables that have been written to.  The key is
            // the worksheet name + table name, and the value is a WrittenTableInfo
            // object that contains information about the table.

            Dictionary<String, WrittenTableInfo> oWrittenTables =
            new Dictionary<String, WrittenTableInfo>();

            // Loop through the columns.

            String sCurrentWorksheetPlusTable = String.Empty;
            ListObject oTable = null;

            foreach (GraphMetricColumn oGraphMetricColumn in graphMetricColumns)
            {
            String sThisWorksheetPlusTable = oGraphMetricColumn.WorksheetName
                + oGraphMetricColumn.TableName;

            if (sThisWorksheetPlusTable != sCurrentWorksheetPlusTable)
            {
                // This is a different table.  Get its ListObject.

                if ( !ExcelUtil.TryGetTable(workbook,
                    oGraphMetricColumn.WorksheetName,
                    oGraphMetricColumn.TableName, out oTable) )
                {
                    // The table couldn't be found.

                    continue;
                }

                sCurrentWorksheetPlusTable = sThisWorksheetPlusTable;
            }

            WrittenTableInfo oWrittenTableInfo;

            if ( !oWrittenTables.TryGetValue(sThisWorksheetPlusTable,
                out oWrittenTableInfo) )
            {
                // Show all the table's columns.  If a graph metric column
                // isn't visible, it can't be written to.

                ExcelHiddenColumns oExcelHiddenColumns =
                    ExcelColumnHider.ShowHiddenColumns(oTable);

                oWrittenTableInfo = new WrittenTableInfo();
                oWrittenTableInfo.Table = oTable;
                oWrittenTableInfo.HiddenColumns = oExcelHiddenColumns;
                oWrittenTableInfo.Cleared = false;

                oWrittenTables.Add(sThisWorksheetPlusTable, oWrittenTableInfo);
            }

            // Apparent Excel bug: Adding a column when the header row is not
            // the default row height increases the header row height.  Work
            // around this by saving the height and restoring it below.

            Double dHeaderRowHeight = (Double)oTable.HeaderRowRange.RowHeight;

            // Write the column.

            Debug.Assert(oTable != null);

            if (oGraphMetricColumn is GraphMetricColumnWithID)
            {
                WriteGraphMetricColumnWithIDToWorkbook(
                    (GraphMetricColumnWithID)oGraphMetricColumn, oTable);
            }
            else if (oGraphMetricColumn is GraphMetricColumnOrdered)
            {
                if (!oWrittenTableInfo.Cleared)
                {
                    // GraphMetricColumnOrdered columns require that the table
                    // be cleared before any graph metric values are written to
                    // it.

                    ExcelUtil.ClearTable(oTable);
                    oWrittenTableInfo.Cleared = true;

                    // Clear AutoFiltering, which interferes with writing an
                    // ordered list to the column.

                    ExcelUtil.ClearTableAutoFilters(oTable);
                }

                WriteGraphMetricColumnOrderedToWorkbook(
                    (GraphMetricColumnOrdered)oGraphMetricColumn, oTable);
            }
            else
            {
                Debug.Assert(false);
            }

            oTable.HeaderRowRange.RowHeight = dHeaderRowHeight;
            }

            // Restore any hidden columns in the tables that were written to.

            foreach (KeyValuePair<String, WrittenTableInfo> oKeyValuePair in
            oWrittenTables)
            {
            WrittenTableInfo oWrittenTableInfo = oKeyValuePair.Value;

            ExcelColumnHider.RestoreHiddenColumns(oWrittenTableInfo.Table,
                oWrittenTableInfo.HiddenColumns);
            }
        }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            // Note regarding cell styles:
            //
            // Versions of NodeXL earlier than 1.0.1.130 didn't merge duplicate
            // edges before calculating graph metrics, and as a result some metrics
            // were invalid.  That was indicated by applying the CellStyleNames.Bad
            // Excel style to the invalid cells.  Starting in version 1.0.1.130
            // there are no longer invalid metrics, but the
            // CellStyleNames.GraphMetricGood style is always applied to those old
            // potentially bad metric cells (instead of null, which uses the
            // current cell style) in case graph metrics are calculated on an old
            // workbook that had bad metric cells.  If null were used, the old Bad
            // style would always remain on the previously bad cells, even if are
            // now filled with good metric values.

            graphMetricColumns = new GraphMetricColumn[0];

            if (!calculateGraphMetricsContext.GraphMetricUserSettings.
                ShouldCalculateGraphMetrics(GraphMetrics.OverallMetrics))
            {
                return(true);
            }

            // Calculate the overall metrics using the OverallMetricCalculator
            // class in the Algorithms namespace, which knows nothing about Excel.

            OverallMetrics oOverallMetrics;

            if (!(new Algorithms.OverallMetricCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker,
                                         out oOverallMetrics))
            {
                // The user cancelled.

                return(false);
            }

            OverallMetricRows oOverallMetricRows = new OverallMetricRows();


            //*********************************
            // Graph type
            //*********************************

            AddRow("Graph Type", oOverallMetrics.Directedness.ToString(),
                   oOverallMetricRows);


            //*********************************
            // Vertex count
            //*********************************

            AddRow(oOverallMetricRows);
            AddRow("Vertices", oOverallMetrics.Vertices, oOverallMetricRows);


            //*********************************
            // Edge counts
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("Unique Edges", oOverallMetrics.UniqueEdges, oOverallMetricRows);

            AddRow("Edges With Duplicates", oOverallMetrics.EdgesWithDuplicates,
                   oOverallMetricRows);

            AddRow("Total Edges", oOverallMetrics.TotalEdges, oOverallMetricRows);


            //*********************************
            // Self-loops
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("Self-Loops", oOverallMetrics.SelfLoops, oOverallMetricRows);


            //*********************************
            // Connected component counts
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("Connected Components", oOverallMetrics.ConnectedComponents,
                   oOverallMetricRows);

            AddRow("Single-Vertex Connected Components",
                   oOverallMetrics.SingleVertexConnectedComponents,
                   oOverallMetricRows);

            AddRow("Maximum Vertices in a Connected Component",
                   oOverallMetrics.MaximumConnectedComponentVertices,
                   oOverallMetricRows);

            AddRow("Maximum Edges in a Connected Component",
                   oOverallMetrics.MaximumConnectedComponentEdges,
                   oOverallMetricRows);


            //*********************************
            // Geodesic distances
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("Maximum Geodesic Distance (Diameter)",

                   NullableToGraphMetricValue <Int32>(
                       oOverallMetrics.MaximumGeodesicDistance),

                   oOverallMetricRows);

            AddRow("Average Geodesic Distance",

                   NullableToGraphMetricValue <Double>(
                       oOverallMetrics.AverageGeodesicDistance),

                   oOverallMetricRows);


            //*********************************
            // Graph density
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("Graph Density",
                   NullableToGraphMetricValue <Double>(oOverallMetrics.GraphDensity),
                   oOverallMetricRows);


            //*********************************
            // NodeXL version
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow("NodeXL Version", AssemblyUtil2.GetFileVersion(),
                   oOverallMetricRows);


            graphMetricColumns = new GraphMetricColumn[] {
                CreateGraphMetricColumnOrdered(
                    OverallMetricsTableColumnNames.Name,
                    oOverallMetricRows.MetricNames),

                CreateGraphMetricColumnOrdered(
                    OverallMetricsTableColumnNames.Value,
                    oOverallMetricRows.MetricValues),
            };

            return(true);
        }
        //*************************************************************************
        //  Method: TryCalculateGraphMetrics()
        //
        /// <summary>
        /// Attempts to calculate a set of one or more related metrics.
        /// </summary>
        ///
        /// <param name="graph">
        /// The graph to calculate metrics for.  The graph may contain duplicate
        /// edges and self-loops.
        /// </param>
        ///
        /// <param name="calculateGraphMetricsContext">
        /// Provides access to objects needed for calculating graph metrics.
        /// </param>
        ///
        /// <param name="graphMetricColumns">
        /// Where an array of GraphMetricColumn objects gets stored if true is
        /// returned, one for each related metric calculated by this method.
        /// </param>
        ///
        /// <returns>
        /// true if the graph metrics were calculated or don't need to be
        /// calculated, false if the user wants to cancel.
        /// </returns>
        ///
        /// <remarks>
        /// This method periodically checks BackgroundWorker.<see
        /// cref="BackgroundWorker.CancellationPending" />.  If true, the method
        /// immediately returns false.
        ///
        /// <para>
        /// It also periodically reports progress by calling the
        /// BackgroundWorker.<see
        /// cref="BackgroundWorker.ReportProgress(Int32, Object)" /> method.  The
        /// userState argument is a <see cref="GraphMetricProgress" /> object.
        /// </para>
        ///
        /// <para>
        /// Calculated metrics for hidden rows are ignored by the caller, because
        /// Excel misbehaves when values are written to hidden cells.
        /// </para>
        ///
        /// </remarks>
        //*************************************************************************
        public override Boolean TryCalculateGraphMetrics(
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
            )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            GraphMetricUserSettings oGraphMetricUserSettings =
            calculateGraphMetricsContext.GraphMetricUserSettings;

            if ( !oGraphMetricUserSettings.ShouldCalculateGraphMetrics(
                GraphMetrics.Degree |
                GraphMetrics.InDegree |
                GraphMetrics.OutDegree
            ) )
            {
            return (true);
            }

            IVertexCollection oVertices = graph.Vertices;
            Int32 iVertices = oVertices.Count;

            // The following lists correspond to vertex worksheet columns.

            List<GraphMetricValueWithID> oInDegreeGraphMetricValues =
            new List<GraphMetricValueWithID>();

            List<GraphMetricValueWithID> oOutDegreeGraphMetricValues =
            new List<GraphMetricValueWithID>();

            List<GraphMetricValueWithID> oDegreeGraphMetricValues =
            new List<GraphMetricValueWithID>();

            // Create a dictionary to keep track of vertex degrees.  The key is the
            // IVertex.ID and the value is a zero-based index into the above lists.

            Dictionary<Int32, Int32> oVertexIDDictionary =
            new Dictionary<Int32, Int32>();

            // Calculate the degrees for each vertex using the
            // VertexDegreeCalculator class in the Algorithms namespace, which
            // knows nothing about Excel.
            //
            // For simplicity, all degree metrics (in-degree, out-degree, and
            // degree) are calculated regardless of whether the graph is directed
            // or undirected.  After all metrics are calculated,
            // FilterGraphMetricColumns() filters out the metrics that don't apply
            // to the graph, based on its directedness.

            Dictionary<Int32, VertexDegrees> oVertexDegreeDictionary;

            if ( !( new Algorithms.VertexDegreeCalculator() ).
            TryCalculateGraphMetrics(graph,
                calculateGraphMetricsContext.BackgroundWorker,
                out oVertexDegreeDictionary) )
            {
            // The user cancelled.

            return (false);
            }

            Int32 iRowID;

            foreach (IVertex oVertex in oVertices)
            {
            if ( !TryGetRowID(oVertex, out iRowID) )
            {
                continue;
            }

            VertexDegrees oVertexDegrees;

            if ( !oVertexDegreeDictionary.TryGetValue(oVertex.ID,
                out oVertexDegrees) )
            {
                Debug.Assert(false);
            }

            oInDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                iRowID, oVertexDegrees.InDegree) );

            oOutDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                iRowID, oVertexDegrees.OutDegree) );

            oDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                iRowID, oVertexDegrees.Degree) );

            Debug.Assert(oInDegreeGraphMetricValues.Count ==
                oOutDegreeGraphMetricValues.Count);

            oVertexIDDictionary.Add(oVertex.ID,
                oInDegreeGraphMetricValues.Count - 1);
            }

            // Figure out which columns to add.

            graphMetricColumns = FilterGraphMetricColumns(graph,
            calculateGraphMetricsContext, oInDegreeGraphMetricValues,
            oOutDegreeGraphMetricValues, oDegreeGraphMetricValues);

            return (true);
        }