Displaying MOLE symbology with the GlobeControl
MainForm.cs
// Copyright 2010 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Diagnostics;
using System.Threading;

using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.GlobeCore;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.SystemUI;

using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.DefenseSolutions;

namespace GlobeControlApp
{
    public sealed partial class MainForm : Form
    {

        #region private class members
        private IGlobeControl m_globeControl = null;
        private IGlobeViewUtil m_globeViewUtil = null;
        #endregion

        #region class constructor
        public MainForm()
        {
            InitializeComponent();
        }
        #endregion

        #region Main Menu event handlers
        private void menuOpenDoc_Click(object sender, EventArgs e)
        {
            //execute Open Document command
            ICommand command = new ControlsGlobeOpenDocCommandClass();
            command.OnCreate(m_globeControl.Object);
            command.OnClick();
        }

        private void menuExitApp_Click(object sender, EventArgs e)
        {
            //exit the application
            Application.Exit();
        }
        #endregion

        /// <summary>
        /// Mouse move event handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void axGlobeControl1_OnMouseMove(object sender, IGlobeControlEvents_OnMouseMoveEvent e)
        {
            double dLon, dLat, dAlt;
            //convert the window coordinate into geographic coordinates
            m_globeViewUtil.WindowToGeographic( m_globeControl.GlobeDisplay,
                                                m_globeControl.GlobeDisplay.ActiveViewer,
                                                e.x,
                                                e.y,
                                                true,
                                                out dLon,
                                                out dLat,
                                                out dAlt);

            //report the mouse geographic coordinate onto the statusbar
            statusBarXY.Text = string.Format("{0} {1} {2}", dLon.ToString("###.###"), dLat.ToString("###.###"), dAlt.ToString("###.###"));
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {

        }

        // =================================================================
        // The following code was added to a default(wizard generated)
    // Globe Control Application in order to obtain a MOLE Layer in 3D,
    // insert a feature, and refresh the layer.
        // =================================================================

        IFeatureClass m_MoleFC = null;
    private static string m_DataPath = null;
    private int m_addedUnitCt = 0;

        /// <summary>
        /// Form Load event handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_Load(object sender, EventArgs e)
        {
            m_globeControl = axGlobeControl1.Object as IGlobeControl;

            //cast the GlobeViewUtil from the GlobeCamera
            m_globeViewUtil = m_globeControl.GlobeCamera as IGlobeViewUtil;
        }

        /// <summary>
        /// Add a "Add Unit" button and use this as its event handler
        /// </summary>
        private void butTest_Click(object sender, EventArgs e)
        {
      if (!MoleFcIsValid())
        return;
      AddUnitToFC( m_MoleFC );
      IGlobeDisplay globeDisplay = axGlobeControl1.GlobeDisplay;
      RefreshMoleLayers( globeDisplay );
    }

        /// <summary>
        /// Add a "Move All Units" button and use this as its event handler
        /// </summary>
        private void butTest2_Click(object sender, EventArgs e)
        {
      if (!MoleFcIsValid())
        return;
      MoveAllUnits( m_MoleFC );
      IGlobeDisplay globeDisplay = axGlobeControl1.GlobeDisplay;
      RefreshMoleLayers( globeDisplay );
    }

        /// <summary>
        /// Refreshes all MOLE Layers in a GlobeDisplay/Globe
        /// </summary>
        private void RefreshMoleLayers(IGlobeDisplay globeDisplay)
        {
      try
      {
                IBasicMap map = globeDisplay.Globe as IBasicMap;

                // find the mole Force Element layers and refresh them
                IEnumLayer enumLayer = map.get_Layers(null, true);
                enumLayer.Reset();
                ILayer layer = null;
                while ((layer = enumLayer.Next()) != null)
                {
                    if (layer is IForceElementLayer)
                    {
                        // Code required to get a MOLE Layer to rebuild & refresh in Globe
                        ICachedGraphicLayer MoleLayer = layer as ICachedGraphicLayer;
                        MoleLayer.Refresh();

                        globeDisplay.SuspendTileFetch();

                        I3DSettings layer3DSettings = MoleLayer as I3DSettings;
                        if ((layer3DSettings != null) && (layer3DSettings.DisplayOption == mole3DDisplayEnum.mole3DDisplayExtrude))
                        {
                            // use optimized refresh
                            ESRI.ArcGIS.Display.IDisplay display = globeDisplay as ESRI.ArcGIS.Display.IDisplay;
                            layer.Draw(esriDrawPhase.esriDPGeography, display, null);
                        }
                        else
                        {
                            // Use normal GlobeDisplay refresh (Does not perform as well)
                            (globeDisplay as IGlobeDisplayLayers).RefreshLayer(layer);
                        }

                        globeDisplay.ResumeTileFetch();
                    }
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine(e.Message);
            }
        }

        /// <summary>
        /// Adds a Unit to a feature class with the MOLE data model
        /// </summary>
        private void AddUnitToFC(IFeatureClass featureClass)
        {
      try
      {
        // Random Number Generation (so each new unit shows up at a different point) 
        const double maxRandom = 5.0;
        System.Random randomGenerator = new System.Random();
        double dRandom = randomGenerator.NextDouble() * maxRandom;

        // create the new feature
        IFeature newFeature = featureClass.CreateFeature();
        // get an insert cursor
        IFeatureCursor insertCursor = featureClass.Insert( false );

        if (newFeature != null)
        {
          // create a random point for the unit's location
          IPoint point = new PointClass();
          point.PutCoords( -117.0 + dRandom, 35.0 + dRandom / 2.0 );
          point.Z = 100.0;

          IZAware za = point as IZAware;
          za.ZAware = true;

          // set the feature's shape
          newFeature.Shape = (point as IGeometry);

          // set feature fields (SIC, name)
          string symbolIdCode = "S";
          char affilCode = 'F';
          
          // Set a different affiliation for each new unit added.
          int affil = m_addedUnitCt % 4;
          switch (affil)
          {
            case 0:
              affilCode = 'F';
              break;
            case 1:
              affilCode = 'H';
              break;
            case 2:
              affilCode = 'N';
              break;
            case 3:
              affilCode = 'U';
              break;
            default:
              break;
          }

          // Set the Symbol ID code.
          symbolIdCode = symbolIdCode + affilCode + "GPUCI-----USG";

          newFeature.set_Value( newFeature.Table.FindField( "Symbol_ID" ), symbolIdCode );

          // Increment the unit counter used for the unit label.
          m_addedUnitCt++;

          // Set the label for the unit's graphic.
          string testName = "Test Insert Unit " + m_addedUnitCt;
          newFeature.set_Value( newFeature.Table.FindField( "Name" ), testName );

          insertCursor.InsertFeature( newFeature as IFeatureBuffer );
        }

        insertCursor.Flush();  // make sure changes are committed before we exit
      }
      catch (Exception e)
      {
        Trace.WriteLine( e.Message );
      }
    }

        /// <summary>
        /// Moves all point geometries in a Feature Class
        /// </summary>
        private void MoveAllUnits(IFeatureClass featureClass)
        {
      try
      {
        // get all features
        IFeatureCursor updateCursor = featureClass.Update( null, false );

        IFeature updateFeature = updateCursor.NextFeature();

        while (updateFeature != null)
        {
          IGeometry geometry = updateFeature.Shape;
          IPoint point = geometry as IPoint;

          // make sure not an empty/corrupt geometry
          if ((geometry != null) && (point != null) && (!geometry.IsEmpty))
          {
            point.X += 0.20;
            updateFeature.Shape = geometry;
            updateCursor.UpdateFeature( updateFeature );
          }

          updateFeature = updateCursor.NextFeature();
        }
      }
      catch (Exception e)
      {
        Trace.WriteLine( e.Message );
      }
    }

        /// <summary>
        /// Loads a MOLE Layer in Globe
        /// </summary>
        /// <remarks>
        /// This is very similar to the 2D usage with additional code to 
        /// set the I3DSettings properties for the layer
        /// </remarks>
        private IFeatureClass LoadMoleFeatureClassInGlobe(IGlobe globe)
        {
            string dbPath = MoleMDB;
            IFeatureClass fc = LoadAccessFeatureClass( dbPath, "FriendlyForces");

            Debug.WriteLine("Attempting to Add MOLE Layer");

            IGeoFeatureLayer pFeatureLayer = new FeatureLayer() as IGeoFeatureLayer;
            pFeatureLayer.FeatureClass = fc;

            //Create a MOLE layer and attach the feature layer to it
            ICachedGraphicFeatureLayer pLayer = new ForceElementLayer() as ICachedGraphicFeatureLayer;
            pLayer.FeatureLayer = pFeatureLayer;

            //Set the size for symbols in the layer     
            IForceElementLayer pForceElementLayer = pLayer as IForceElementLayer;
            pForceElementLayer.Size = 0.20;
      
            // Set 3D Settings
            I3DSettings p3DSettings = pForceElementLayer as I3DSettings;
            p3DSettings.DisplayOption = mole3DDisplayEnum.mole3DDisplayExtrude; // or .mole3DDisplayBoth; // or .mole3DDisplayDrape
            p3DSettings.EnableCallouts = true;
            p3DSettings.DefaultElevationMeters = 10000;

            ILayer layer = pLayer as ILayer;
            layer.Name = "MOLE 3D Unit Layer";

            globe.AddLayerType(layer,
                ESRI.ArcGIS.GlobeCore.esriGlobeLayerType.esriGlobeLayerTypeDraped,
                true);

            System.Diagnostics.Debug.WriteLine("Layer Added");

            return fc;
        }

        /// <summary>
        /// Opens a Feature Class from an Access PGDB
        /// </summary>
        private IFeatureClass LoadAccessFeatureClass(string dbPath, string featureClassName)
        {
            // open the access database
            IWorkspaceFactory pWSF = new AccessWorkspaceFactoryClass();
            IWorkspace pWS = pWSF.OpenFromFile(dbPath, 0);

            // make sure the database was opened
            if (pWS == null)
            {
                MessageBox.Show("Could not locate and/or open database at: \"" + dbPath + "\"", "Error!");
                return null;
            }

            IFeatureWorkspace pFWS = pWS as IFeatureWorkspace;
            IFeatureClass pFC;
            lock (pFWS) // lock to prevent multi-process access
            {
                // open the feature class
                pFC = pFWS.OpenFeatureClass(featureClassName);
            }

            // make sure the feature class was opened
            if (pFC == null)
            {
                MessageBox.Show("Could not open feature class: " + featureClassName + ".", "Error!");
                return null;
            }

            return pFC;
        }

        /// <summary>
        /// Path to ArcGIS Install
        /// </summary>
        private static string GetSdkDataPath()
        {
            //get the ArcGIS path from the registry
            Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\ESRI\ArcGIS_SXS_SDK");
            string path = Convert.ToString(key.GetValue("InstallDir"));

            //set the of the logo
            string str = System.IO.Path.Combine(path, @"Samples\data\");

            if (!System.IO.Directory.Exists(str))
            {
                MessageBox.Show("Path :" + str + " does not exist!");
                return string.Empty;
            }

            return str;
        }

        /// <summary>
        /// Path to MOLE Globe Access PGDB data file
        /// </summary>
        public static string MoleMDB
        {
      get
            {
                if (m_DataPath == null)
                {
                    m_DataPath = GetSdkDataPath() + @"MilitaryOverlayEditor\mole_globe.mdb";
                }

                return m_DataPath;
            }
        }

    // Checks if a valid MOLE feature class has been loaded onto the globe.
    private bool MoleFcIsValid()
    {
      if (m_MoleFC == null)
      {
        MessageBox.Show("Please open an ArcGlobe (.3dd) document\nbefore pressing this button.",
                       "MOLE Globe", MessageBoxButtons.OK, MessageBoxIcon.Information);
        return false;
      }
      return true;
    }

    // Called when a Globe document is opened.
        private void axGlobeControl1_OnGlobeReplaced(object sender, IGlobeControlEvents_OnGlobeReplacedEvent e)
        {
            // Load the MOLE Unit Feature Class
            m_MoleFC = LoadMoleFeatureClassInGlobe(this.axGlobeControl1.Globe as IGlobe);
        }
    }
}