About the 3D dynamic element tracking Sample
[C#]
TrackDynamicObject.cs
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.DataSourcesFile;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.GlobeCore;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Analyst3D;
namespace GlobeDynamicObjectTracking
{
/// <summary>
/// This command demonstrates tracking dynamic object in ArcGlobe/GlobeControl with the camera
/// </summary>
[Guid("DCB871A1-390A-456f-8A0D-9FDB6A20F721")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("GlobeControlApp.TrackDynamicObject")]
public sealed class TrackDynamicObject : BaseCommand, IDisposable
{
#region COM Registration Function(s)
[ComRegisterFunction()]
[ComVisible(false)]
static void RegisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryRegistration(registerType);
//
// TODO: Add any COM registration code here
//
}
[ComUnregisterFunction()]
[ComVisible(false)]
static void UnregisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryUnregistration(registerType);
//
// TODO: Add any COM unregistration code here
//
}
#region ArcGIS Component Category Registrar generated code
/// <summary>
/// Required method for ArcGIS Component Category registration -
/// Do not modify the contents of this method with the code editor.
/// </summary>
private static void ArcGISCategoryRegistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
GMxCommands.Register(regKey);
ControlsCommands.Register(regKey);
}
/// <summary>
/// Required method for ArcGIS Component Category unregistration -
/// Do not modify the contents of this method with the code editor.
/// </summary>
private static void ArcGISCategoryUnregistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
GMxCommands.Unregister(regKey);
ControlsCommands.Unregister(regKey);
}
#endregion
#endregion
//class members
private IGlobeHookHelper m_globeHookHelper = null;
private IGlobeDisplay m_globeDisplay = null;
private ISceneViewer m_sceneViwer = null;
private IGlobeGraphicsLayer m_globeGraphicsLayer = null;
private IRealTimeFeedManager m_realTimeFeedManager = null;
private IRealTimeFeed m_realTimeFeed = null;
private bool m_bConnected = false;
private bool m_bTrackAboveTarget = true;
private bool m_once = true;
private int m_trackObjectIndex = -1;
private string m_shapefileName = string.Empty;
#region Ctor
/// <summary>
/// Class Ctor
/// </summary>
public TrackDynamicObject()
{
base.m_category = ".NET Samples";
base.m_caption = "Track Dynamic Object";
base.m_message = "Tracking a dynamic object";
base.m_toolTip = "Track Dynamic Object";
base.m_name = base.m_category + "_" + base.m_caption;
try
{
string bitmapResourceName = GetType().Name + ".bmp";
base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
}
}
#endregion
#region Overriden Class Methods
/// <summary>
/// Occurs when this command is created
/// </summary>
/// <param name="hook">Instance of the application</param>
public override void OnCreate(object hook)
{
//initialize the hook-helper
if (m_globeHookHelper == null)
m_globeHookHelper = new GlobeHookHelper();
//set the hook
m_globeHookHelper.Hook = hook;
//get the ArcGIS path from the registry
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\ESRI\ArcObjectsSDK10.1");
string path = Convert.ToString(key.GetValue("InstallDir"));
//set the path to the featureclass used by the GPS simulator
m_shapefileName = System.IO.Path.Combine(path, "Samples\\data\\USAMajorHighways\\usa_major_highways.shp");
//get the GlobeDisplsy from the hook helper
m_globeDisplay = m_globeHookHelper.GlobeDisplay;
//initialize the real-time manager
if (null == m_realTimeFeedManager)
m_realTimeFeedManager = new RealTimeFeedManagerClass();
//use the built in simulator of the real-time manager
m_realTimeFeedManager.RealTimeFeed = m_realTimeFeedManager.RealTimeFeedSimulator as IRealTimeFeed;
m_realTimeFeed = m_realTimeFeedManager.RealTimeFeed;
}
/// <summary>
/// Occurs when this command is clicked
/// </summary>
public override void OnClick()
{
try
{
if (!m_bConnected)
{
//show the tracking type selection dialog (whether to track the element from above or follow it from behind)
TrackSelectionDlg dlg = new TrackSelectionDlg();
if (System.Windows.Forms.DialogResult.OK != dlg.ShowDialog())
return;
//get the required tracking mode
m_bTrackAboveTarget = dlg.UseOrthoTrackingMode;
//do only once initializations
if (m_once)
{
//create the graphics layer to manage the dynamic object
m_globeGraphicsLayer = new GlobeGraphicsLayerClass();
((ILayer)m_globeGraphicsLayer).Name = "DynamicObjects";
IScene scene = (IScene)m_globeDisplay.Globe;
//add the new graphic layer to the globe
scene.AddLayer((ILayer)m_globeGraphicsLayer, false);
//activate the graphics layer
scene.ActiveGraphicsLayer = (ILayer)m_globeGraphicsLayer;
//redraw the GlobeDisplay
m_globeDisplay.RefreshViewers();
//open a polyline featurelayer that would serve the real-time feed GPS simulator
IFeatureLayer featureLayer = GetFeatureLayer();
if (featureLayer == null)
return;
//assign the featurelayer to the GPS simulator
m_realTimeFeedManager.RealTimeFeedSimulator.FeatureLayer = featureLayer;
m_once = false;
}
//get the GlobeViewUtil which is needed for coordinate transformations
m_sceneViwer = m_globeDisplay.ActiveViewer;
//Set the globe mode to terrain mode, since otherwise it will not be possible to set the target position
((IGlobeCamera)m_sceneViwer.Camera).OrientationMode = esriGlobeCameraOrientationMode.esriGlobeCameraOrientationLocal;
//set the simulator elapsed time
m_realTimeFeedManager.RealTimeFeedSimulator.TimeIncrement = 0.1; //sec
//wire the real-time feed PositionUpdate event
((IRealTimeFeedEvents_Event)m_realTimeFeed).PositionUpdated += new IRealTimeFeedEvents_PositionUpdatedEventHandler(OnPositionUpdated);
//start the real-time listener
m_realTimeFeed.Start();
}
else
{
//stop the real-time listener
m_realTimeFeed.Stop();
//un-wire the PositionUpdated event handler
((IRealTimeFeedEvents_Event)m_realTimeFeed).PositionUpdated -= new IRealTimeFeedEvents_PositionUpdatedEventHandler(OnPositionUpdated);
}
//switch the connection flag
m_bConnected = !m_bConnected;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
}
}
/// <summary>
/// The Checked property indicates the state of this Command.
/// </summary>
/// <remarks>If a command item appears depressed on a commandbar, the command is checked.</remarks>
public override bool Checked
{
get
{
return m_bConnected;
}
}
#endregion
#region helper methods
/// <summary>
/// get a featurelayer that would be used by the real-time simulator
/// </summary>
/// <returns></returns>
private IFeatureLayer GetFeatureLayer()
{
//instantiate a new featurelayer
IFeatureLayer featureLayer = new FeatureLayerClass();
//set the layer's name
featureLayer.Name = "GPS Data";
//open the featureclass
IFeatureClass featureClass = OpenFeatureClass();
if (featureClass == null)
return null;
//set the featurelayer featureclass
featureLayer.FeatureClass = featureClass;
//return the featurelayer
return featureLayer;
}
/// <summary>
/// Opens a shapefile polyline featureclass
/// </summary>
/// <returns></returns>
private IFeatureClass OpenFeatureClass()
{
string fileName = System.IO.Path.GetFileNameWithoutExtension(m_shapefileName);
//instantiate a new workspace factory
IWorkspaceFactory workspaceFactory = new ShapefileWorkspaceFactoryClass();
//get the workspace directory
string path = System.IO.Path.GetDirectoryName(m_shapefileName);
//open the workspace containing the featureclass
IFeatureWorkspace featureWorkspace = workspaceFactory.OpenFromFile(path, 0) as IFeatureWorkspace;
//open the featureclass
IFeatureClass featureClass = featureWorkspace.OpenFeatureClass(fileName);
//make sure that the featureclass type is polyline
if (featureClass.ShapeType != esriGeometryType.esriGeometryPolyline)
{
featureClass = null;
}
//return the featureclass
return featureClass;
}
/// <summary>
/// Adds a sphere element to the given graphics layer at the specified position
/// </summary>
/// <param name="globeGraphicsLayer"></param>
/// <param name="position"></param>
/// <returns></returns>
private int AddTrackElement(IGlobeGraphicsLayer globeGraphicsLayer, esriGpsPositionInfo position)
{
if (null == globeGraphicsLayer)
return -1;
//create a new point at the given position
IPoint point = new PointClass();
((IZAware)point).ZAware = true;
point.X = position.longitude;
point.Y = position.latitude;
point.Z = 0.0;
//set the color for the element (red)
IRgbColor color = new RgbColorClass();
color.Red = 255;
color.Green = 0;
color.Blue = 0;
//create a new 3D marker symbol
IMarkerSymbol markerSymbol = new SimpleMarker3DSymbolClass();
//set the marker symbol's style and resolution
((ISimpleMarker3DSymbol)markerSymbol).Style = esriSimple3DMarkerStyle.esriS3DMSSphere;
((ISimpleMarker3DSymbol)markerSymbol).ResolutionQuality = 1.0;
//set the symbol's size and color
markerSymbol.Size = 700;
markerSymbol.Color = color as IColor;
//crate the graphic element
IElement trackElement = new MarkerElementClass();
//set the element's symbol and geometry (location and shape)
((IMarkerElement)trackElement).Symbol = markerSymbol;
trackElement.Geometry = point as IPoint;
//add the element to the graphics layer
int elemIndex = 0;
((IGraphicsContainer)globeGraphicsLayer).AddElement(trackElement, 0);
//get the element's index
globeGraphicsLayer.FindElementIndex(trackElement, out elemIndex);
return elemIndex;
}
/// <summary>
/// The real-time feed position updated event handler
/// </summary>
/// <param name="position">a GPS position information</param>
/// <param name="estimate">indicates whether this is an estimated time or real time</param>
void OnPositionUpdated(ref esriGpsPositionInfo position, bool estimate)
{
try
{
//add the tracking element to the tracking graphics layer (should happen only once)
if (-1 == m_trackObjectIndex)
{
int index = AddTrackElement(m_globeGraphicsLayer, position);
if (-1 == index)
throw new Exception("could not add tracking object");
//cache the element's index
m_trackObjectIndex = index;
return;
}
//get the element by its index
IElement elem = ((IGraphicsContainer3D)m_globeGraphicsLayer).get_Element(m_trackObjectIndex);
//keep the previous location
double lat, lon, alt;
((IPoint)elem.Geometry).QueryCoords(out lon, out lat);
alt = ((IPoint)elem.Geometry).Z;
//update the element's position
IPoint point = elem.Geometry as IPoint;
point.X = position.longitude;
point.Y = position.latitude;
point.Z = alt;
elem.Geometry = (IGeometry)point;
//update the element in the graphics layer.
lock (m_globeGraphicsLayer)
{
m_globeGraphicsLayer.UpdateElementByIndex(m_trackObjectIndex);
}
IGlobeCamera globeCamera = m_sceneViwer.Camera as IGlobeCamera;
//set the camera position in order to track the element
if (m_bTrackAboveTarget)
TrackAboveTarget(globeCamera, point);
else
TrackFollowTarget(globeCamera, point.X, point.Y, point.Z, lon, lat, alt);
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
}
}
void TrackDynamicObject_PositionUpdated(ref esriGpsPositionInfo position, bool estimate)
{
}
/// <summary>
/// If the user chose to track the element from behind, set the camera behind the element
/// so that the camera will be placed on the line connecting the previous and the current element's position.
/// </summary>
/// <param name="globeCamera"></param>
/// <param name="newLon"></param>
/// <param name="newLat"></param>
/// <param name="newAlt"></param>
/// <param name="oldLon"></param>
/// <param name="oldLat"></param>
/// <param name="oldAlt"></param>
private void TrackFollowTarget(IGlobeCamera globeCamera, double newLon, double newLat, double newAlt, double oldLon, double oldLat, double oldAlt)
{
//make sure that the camera position is not directly above the element. Otherwise it can lead to
//an ill condition
if (newLon == oldLon && newLat == oldLat)
{
newLon += 0.00001;
newLat += 0.00001;
}
//calculate the azimuth from the previous position to the current position
double azimuth = Math.Atan2(newLat - oldLat, newLon - oldLon) * (Math.PI / 180.0);
//the camera new position, right behind the element
double obsX = newLon - 0.04 * Math.Cos(azimuth * (Math.PI / 180));
double obsY = newLat - 0.04 * Math.Sin(azimuth * (Math.PI / 180));
//set the camera position. The camera must be locked in order to prevent a dead-lock caused by the cache manager
lock (globeCamera)
{
globeCamera.SetTargetLatLonAlt(newLat, newLon, newAlt / 1000.0);
globeCamera.SetObserverLatLonAlt(obsY, obsX, newAlt / 1000.0 + 0.7);
m_sceneViwer.Camera.Apply();
}
//refresh the globe display
m_globeDisplay.RefreshViewers();
}
/// <summary>
/// should the user choose to track the element from above, set the camera above the element
/// </summary>
/// <param name="globeCamera"></param>
/// <param name="objectLocation"></param>
private void TrackAboveTarget(IGlobeCamera globeCamera, IPoint objectLocation)
{
//Update the observer as well as the camera position
//The camera must be locked in order to prevent a dead-lock caused by the cache manager
lock (globeCamera)
{
globeCamera.SetTargetLatLonAlt(objectLocation.Y, objectLocation.X, objectLocation.Z / 1000.0);
//The camera must nut be located exactly above the target, since it results in poor orientation computation
//and therefore the camera gets jumpy.
globeCamera.SetObserverLatLonAlt(objectLocation.Y - 0.000001, objectLocation.X - 0.000001, objectLocation.Z / 1000.0 + 30.0);
m_sceneViwer.Camera.Apply();
}
m_globeDisplay.RefreshViewers();
}
#endregion
#region IDisposable Members
public void Dispose()
{
}
#endregion
}
}
[Visual Basic .NET]
TrackDynamicObject.vb
Imports Microsoft.VisualBasic
Imports System
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports ESRI.ArcGIS.ADF.BaseClasses
Imports ESRI.ArcGIS.ADF.CATIDs
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.DataSourcesFile
Imports ESRI.ArcGIS.Display
Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.SystemUI
Imports ESRI.ArcGIS.GlobeCore
Imports ESRI.ArcGIS.Controls
Imports ESRI.ArcGIS.Analyst3D
''' <summary>
''' This command demonstrates tracking dynamic object in ArcGlobe/GlobeControl with the camera
''' </summary>
<Guid("DCB871A1-390A-456f-8A0D-9FDB6A20F721"), ClassInterface(ClassInterfaceType.None), ProgId("GlobeControlApp.TrackDynamicObject")> _
Public NotInheritable Class TrackDynamicObject : Inherits BaseCommand : Implements IDisposable
#Region "COM Registration Function(s)"
<ComRegisterFunction(), ComVisible(False)> _
Private Shared Sub RegisterFunction(ByVal registerType As Type)
' Required for ArcGIS Component Category Registrar support
ArcGISCategoryRegistration(registerType)
'
' TODO: Add any COM registration code here
''
End Sub
<ComUnregisterFunction(), ComVisible(False)> _
Private Shared Sub UnregisterFunction(ByVal registerType As Type)
' Required for ArcGIS Component Category Registrar support
ArcGISCategoryUnregistration(registerType)
'
' TODO: Add any COM unregistration code here
''
End Sub
#Region "ArcGIS Component Category Registrar generated code"
''' <summary>
''' Required method for ArcGIS Component Category registration -
''' Do not modify the contents of this method with the code editor.
''' </summary>
Private Shared Sub ArcGISCategoryRegistration(ByVal registerType As Type)
Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
GMxCommands.Register(regKey)
ControlsCommands.Register(regKey)
End Sub
''' <summary>
''' Required method for ArcGIS Component Category unregistration -
''' Do not modify the contents of this method with the code editor.
''' </summary>
Private Shared Sub ArcGISCategoryUnregistration(ByVal registerType As Type)
Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
GMxCommands.Unregister(regKey)
ControlsCommands.Unregister(regKey)
End Sub
#End Region
#End Region
'class members
Private m_globeHookHelper As IGlobeHookHelper = Nothing
Private m_globeDisplay As IGlobeDisplay = Nothing
Private m_sceneViwer As ISceneViewer = Nothing
Private m_globeGraphicsLayer As IGlobeGraphicsLayer = Nothing
Private m_realTimeFeedManager As IRealTimeFeedManager = Nothing
Private m_realTimeFeed As IRealTimeFeed = Nothing
Private m_bConnected As Boolean = False
Private m_bTrackAboveTarget As Boolean = True
Private m_once As Boolean = True
Private m_trackObjectIndex As Integer = -1
Private m_shapefileName As String = String.Empty
#Region "class constructor"
''' <summary>
''' Class Ctor
''' </summary>
Public Sub New()
MyBase.m_category = ".NET Samples"
MyBase.m_caption = "Track Dynamic Object"
MyBase.m_message = "Tracking a dynamic object"
MyBase.m_toolTip = "Track Dynamic Object"
MyBase.m_name = MyBase.m_category & "_" & MyBase.m_caption
Try
Dim bitmapResourceName As String = Me.GetType().Name & ".bmp"
MyBase.m_bitmap = New Bitmap(Me.GetType(), bitmapResourceName)
Catch ex As Exception
System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap")
End Try
End Sub
#End Region
#Region "Overridden Class Methods"
''' <summary>
''' Occurs when this command is created
''' </summary>
''' <param name="hook">Instance of the application</param>
Public Overrides Sub OnCreate(ByVal hook As Object)
'initialize the hook-helper
If m_globeHookHelper Is Nothing Then
m_globeHookHelper = New GlobeHookHelper()
End If
'set the hook
m_globeHookHelper.Hook = hook
'connect to the ZipCodes featureclass
'get the ArcGIS path from the registry
Dim key As RegistryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\ESRI\ArcObjectsSDK10.1")
Dim path As String = Convert.ToString(key.GetValue("InstallDir"))
'set the path to the featureclass used by the GPS simulator
m_shapefileName = System.IO.Path.Combine(path, "Samples\\data\\USAMajorHighways\\usa_major_highways.shp")
'get the GlobeDisplsy from the hook helper
m_globeDisplay = m_globeHookHelper.GlobeDisplay
'initialize the real-time manager
If Nothing Is m_realTimeFeedManager Then
m_realTimeFeedManager = New RealTimeFeedManagerClass()
End If
'use the built in simulator of the real-time manager
m_realTimeFeedManager.RealTimeFeed = TryCast(m_realTimeFeedManager.RealTimeFeedSimulator, IRealTimeFeed)
'keep a reference to the RealTimeManager in order to prevent the garbage collector to try and dispose it
m_realTimeFeed = m_realTimeFeedManager.RealTimeFeed
End Sub
''' <summary>
''' Occurs when this command is clicked
''' </summary>
Public Overrides Sub OnClick()
Try
If (Not m_bConnected) Then
'show the tracking type selection dialog (whether to track the element from above or follow it from behind)
Dim dlg As TrackSelectionDlg = New TrackSelectionDlg()
If System.Windows.Forms.DialogResult.OK <> dlg.ShowDialog() Then
Return
End If
'get the required tracking mode
m_bTrackAboveTarget = dlg.UseOrthoTrackingMode
'do only once initializations
If m_once Then
'create the graphics layer to manage the dynamic object
m_globeGraphicsLayer = New GlobeGraphicsLayerClass()
CType(m_globeGraphicsLayer, ILayer).Name = "DynamicObjects"
Dim scene As IScene = CType(m_globeDisplay.Globe, IScene)
'add the new graphic layer to the globe
scene.AddLayer(CType(m_globeGraphicsLayer, ILayer), False)
'activate the graphics layer
scene.ActiveGraphicsLayer = CType(m_globeGraphicsLayer, ILayer)
'open a polyline featurelayer that would serve the real-time feed GPS simulator
Dim featureLayer As IFeatureLayer = GetFeatureLayer()
If featureLayer Is Nothing Then
Return
End If
'assign the featurelayer to the GPS simulator
m_realTimeFeedManager.RealTimeFeedSimulator.FeatureLayer = featureLayer
m_once = False
End If
'get the GlobeViewUtil which is needed for coordinate transformations
m_sceneViwer = m_globeDisplay.ActiveViewer
'Set the globe mode to terrain mode, since otherwise it will not be possible to set the target position
CType(m_sceneViwer.Camera, IGlobeCamera).OrientationMode = esriGlobeCameraOrientationMode.esriGlobeCameraOrientationLocal
'set the simulator elapsed time
m_realTimeFeedManager.RealTimeFeedSimulator.TimeIncrement = 0.1 'sec
'wire the real-time feed PositionUpdate event
AddHandler (CType(m_realTimeFeed, IRealTimeFeedEvents_Event)).PositionUpdated, AddressOf OnPositionUpdated
'start the real-time listener
m_realTimeFeed.Start()
Else
'stop the real-time listener
m_realTimeFeed.Stop()
'unhook the PositionUpdated event handler
RemoveHandler (CType(m_realTimeFeed, IRealTimeFeedEvents_Event)).PositionUpdated, AddressOf TrackDynamicObject_PositionUpdated
End If
'switch the connection flag
m_bConnected = Not m_bConnected
Catch ex As Exception
System.Diagnostics.Trace.WriteLine(ex.Message)
End Try
End Sub
''' <summary>
''' The Checked property indicates the state of this Command.
''' </summary>
''' <remarks>If a command item appears depressed on a commandbar, the command is checked.</remarks>
Public Overrides ReadOnly Property Checked() As Boolean
Get
Return m_bConnected
End Get
End Property
#End Region
#Region "helper methods"
''' <summary>
''' get a featurelayer that would be used by the real-time simulator
''' </summary>
''' <returns></returns>
Private Function GetFeatureLayer() As IFeatureLayer
'instantiate a new featurelayer
Dim featureLayer As IFeatureLayer = New FeatureLayerClass()
'set the layer's name
featureLayer.Name = "GPS Data"
'open the featureclass
Dim featureClass As IFeatureClass = OpenFeatureClass()
If featureClass Is Nothing Then
Return Nothing
End If
'set the featurelayer featureclass
featureLayer.FeatureClass = featureClass
'return the featurelayer
Return featureLayer
End Function
''' <summary>
''' Opens a shapefile polyline featureclass
''' </summary>
''' <returns></returns>
Private Function OpenFeatureClass() As IFeatureClass
Dim path As String = System.IO.Path.GetDirectoryName(m_shapefileName)
Dim fileName As String = System.IO.Path.GetFileNameWithoutExtension(m_shapefileName)
'instantiate a new workspace factory
Dim workspaceFactory As IWorkspaceFactory = New ShapefileWorkspaceFactoryClass()
'open the workspace containing the featureclass
Dim featureWorkspace As IFeatureWorkspace = TryCast(workspaceFactory.OpenFromFile(path, 0), IFeatureWorkspace)
'open the featureclass
Dim featureClass As IFeatureClass = featureWorkspace.OpenFeatureClass(fileName)
'make sure that the featureclass type is polyline
If featureClass.ShapeType <> esriGeometryType.esriGeometryPolyline Then
featureClass = Nothing
End If
'return the featureclass
Return featureClass
End Function
''' <summary>
''' Adds a sphere element to the given graphics layer at the specified position
''' </summary>
''' <param name="globeGraphicsLayer"></param>
''' <param name="position"></param>
''' <returns></returns>
Private Function AddTrackElement(ByVal globeGraphicsLayer As IGlobeGraphicsLayer, ByVal position As esriGpsPositionInfo) As Integer
If Nothing Is globeGraphicsLayer Then
Return -1
End If
'create a new point at the given position
Dim point As IPoint = New PointClass()
CType(point, IZAware).ZAware = True
point.X = position.longitude
point.Y = position.latitude
point.Z = 0.0
'set the color for the element (red)
Dim color As IRgbColor = New RgbColorClass()
color.Red = 255
color.Green = 0
color.Blue = 0
'create a new 3D marker symbol
Dim markerSymbol As IMarkerSymbol = New SimpleMarker3DSymbolClass()
'set the marker symbol's style and resolution
CType(markerSymbol, ISimpleMarker3DSymbol).Style = esriSimple3DMarkerStyle.esriS3DMSSphere
CType(markerSymbol, ISimpleMarker3DSymbol).ResolutionQuality = 1.0
'set the symbol's size and color
markerSymbol.Size = 700
markerSymbol.Color = TryCast(color, IColor)
'crate the graphic element
Dim trackElement As IElement = New MarkerElementClass()
'set the element's symbol and geometry (location and shape)
CType(trackElement, IMarkerElement).Symbol = markerSymbol
trackElement.Geometry = TryCast(point, IPoint)
'add the element to the graphics layer
Dim elemIndex As Integer = 0
CType(globeGraphicsLayer, IGraphicsContainer).AddElement(trackElement, 0)
'get the element's index
globeGraphicsLayer.FindElementIndex(trackElement, elemIndex)
Return elemIndex
End Function
''' <summary>
''' The real-time feed position updated event handler
''' </summary>
''' <param name="position">a GPS position information</param>
''' <param name="estimate">indicates whether this is an estimated time or real time</param>
Private Sub OnPositionUpdated(ByRef position As esriGpsPositionInfo, ByVal estimate As Boolean)
Try
'add the tracking element to the tracking graphics layer (should happen only once)
If -1 = m_trackObjectIndex Then
Dim index As Integer = AddTrackElement(m_globeGraphicsLayer, position)
If -1 = index Then
Throw New Exception("could not add tracking object")
End If
'cache the element's index
m_trackObjectIndex = index
Return
End If
'get the element by its index
Dim elem As IElement = (CType(m_globeGraphicsLayer, IGraphicsContainer3D)).Element(m_trackObjectIndex)
'keep the previous location
Dim lat, lon, alt As Double
CType(elem.Geometry, IPoint).QueryCoords(lon, lat)
alt = (CType(elem.Geometry, IPoint)).Z
'update the element's position
Dim point As IPoint = TryCast(elem.Geometry, IPoint)
point.X = position.longitude
point.Y = position.latitude
point.Z = alt
elem.Geometry = CType(point, IGeometry)
'update the element in the graphics layer.
SyncLock m_globeGraphicsLayer
m_globeGraphicsLayer.UpdateElementByIndex(m_trackObjectIndex)
End SyncLock
Dim globeCamera As IGlobeCamera = TryCast(m_sceneViwer.Camera, IGlobeCamera)
'set the camera position in order to track the element
If m_bTrackAboveTarget Then
TrackAboveTarget(globeCamera, point)
Else
TrackFollowTarget(globeCamera, point.X, point.Y, point.Z, lon, lat, alt)
End If
Catch ex As Exception
System.Diagnostics.Trace.WriteLine(ex.Message)
End Try
End Sub
Private Sub TrackDynamicObject_PositionUpdated(ByRef position As esriGpsPositionInfo, ByVal estimate As Boolean)
End Sub
''' <summary>
''' If the user chose to track the element from behind, set the camera behind the element
''' so that the camera will be placed on the line connecting the previous and the current element's position.
''' </summary>
''' <param name="globeCamera"></param>
''' <param name="newLon"></param>
''' <param name="newLat"></param>
''' <param name="newAlt"></param>
''' <param name="oldLon"></param>
''' <param name="oldLat"></param>
''' <param name="oldAlt"></param>
Private Sub TrackFollowTarget(ByVal globeCamera As IGlobeCamera, ByVal newLon As Double, ByVal newLat As Double, ByVal newAlt As Double, ByVal oldLon As Double, ByVal oldLat As Double, ByVal oldAlt As Double)
'make sure that the camera position is not directly above the element. Otherwise it can lead to
'an ill condition
If newLon = oldLon AndAlso newLat = oldLat Then
newLon += 0.00001
newLat += 0.00001
End If
'calculate the azimuth from the previous position to the current position
Dim azimuth As Double = Math.Atan2(newLat - oldLat, newLon - oldLon) * (Math.PI / 180.0)
'the camera new position, right behind the element
Dim obsX As Double = newLon - 0.04 * Math.Cos(azimuth * (Math.PI / 180))
Dim obsY As Double = newLat - 0.04 * Math.Sin(azimuth * (Math.PI / 180))
'set the camera position. The camera must be locked in order to prevent a dead-lock caused by the cache manager
SyncLock globeCamera
globeCamera.SetTargetLatLonAlt(newLat, newLon, newAlt / 1000.0)
globeCamera.SetObserverLatLonAlt(obsY, obsX, newAlt / 1000.0 + 0.7)
m_sceneViwer.Camera.Apply()
End SyncLock
'refresh the globe display
m_globeDisplay.RefreshViewers()
End Sub
''' <summary>
''' should the user choose to track the element from above, set the camera above the element
''' </summary>
''' <param name="globeCamera"></param>
''' <param name="objectLocation"></param>
Private Sub TrackAboveTarget(ByVal globeCamera As IGlobeCamera, ByVal objectLocation As IPoint)
'Update the observer as well as the camera position
'The camera must be locked in order to prevent a dead-lock caused by the cache manager
SyncLock globeCamera
globeCamera.SetTargetLatLonAlt(objectLocation.Y, objectLocation.X, objectLocation.Z / 1000.0)
'The camera must nut be located exactly above the target, since it results in poor orientation computation
'and therefore the camera gets jumpy.
globeCamera.SetObserverLatLonAlt(objectLocation.Y - 0.000001, objectLocation.X - 0.000001, objectLocation.Z / 1000.0 + 30.0)
m_sceneViwer.Camera.Apply()
End SyncLock
m_globeDisplay.RefreshViewers()
End Sub
#End Region
#Region "IDisposable Members"
Public Sub Dispose() Implements IDisposable.Dispose
End Sub
#End Region
End Class