About the Dynamic biking Sample
[C#]
DynamicBikingCmd.cs
using System;
using System.IO;
using System.Drawing;
using System.Xml;
using System.Xml.XPath;
using System.Threading;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.esriSystem;
namespace DynamicBiking
{
/// <summary>
/// Summary description for DynamicBikingCmd.
/// </summary>
[Guid("f01054d2-0130-4124-8436-1bf2942bf2b6")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("DynamicBiking.DynamicBikingCmd")]
public sealed class DynamicBikingCmd : BaseCommand
{
#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);
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);
ControlsCommands.Unregister(regKey);
}
#endregion
#endregion
#region class members
private enum GPSPlaybackFormat
{
HST = 0,
GPX = 1,
XML = 2
}
private GPSPlaybackFormat m_playbackFormat = GPSPlaybackFormat.GPX;
private IHookHelper m_hookHelper;
private IDynamicMap m_dynamicMap = null;
private IActiveView m_activeView = null;
private bool m_bConnected = false;
private IPoint m_gpsPosition = null;
private IPoint m_additionalInfoPoint = null;
private IPointCollection4 m_bikeRouteGeometry = null;
private IGeometryBridge2 m_geometryBridge = null;
private WKSPoint[] m_wksPoints = new WKSPoint[1];
private WKSPoint m_wksPrevPosition;
private IDynamicSymbolProperties2 m_dynamicSymbolProperties = null;
private IDynamicCompoundMarker2 m_dynamicCompoundMarker = null;
private IDynamicScreenDisplay m_dynamicScreenDisplay = null;
private IDynamicGlyph m_bikeGlyph = null;
private IDynamicGlyph m_bikeRouteGlyph = null;
private IDynamicGlyph m_textGlyph = null;
private IDynamicGlyph m_catGlyph = null;
private IDynamicGlyph m_gpsGlyph = null;
private IDynamicGlyph[] m_heartRateGlyph;
private float m_gpsSymbolScale = 1.0f;
private double m_heading = 0;
private string m_heartRateString = string.Empty;
private string m_altitudeString = string.Empty;
private string m_speed = string.Empty;
private bool m_bOnce = true;
private int m_heartRateCounter = 0;
private int m_drawCycles = 0;
public int m_playbackSpeed = 10;
private bool m_bTrackMode = false;
string[] nullString = null;
private string m_xmlPath = string.Empty;
// xml loader thread stuff
private Thread m_dataLoaderThread = null;
private static AutoResetEvent m_autoEvent = new AutoResetEvent(false);
private int m_bikePositionCount = 0;
private sealed class BikePositionInfo
{
public BikePositionInfo()
{
}
public WKSPoint position;
public DateTime time;
public double altitudeMeters;
public int heartRate;
public int lapCount;
public int lapAverageHeartRate;
public int lapMaximumHeartRate;
public int lapCalories;
public double lapMaximumSpeed;
public double lapDistanceMeters;
public double course;
public double speed;
public int positionCount;
}
private BikePositionInfo m_bikePositionInfo = null;
private struct XmlDocTaksData
{
public string xmlDocPath;
}
#endregion
#region class constructor
public DynamicBikingCmd()
{
base.m_category = ".NET Samples";
base.m_caption = "Dynamic Biking";
base.m_message = "Dynamic Biking";
base.m_toolTip = "Dynamic Biking";
base.m_name = "DynamicBiking_DynamicBikingCmd";
try
{
string bitmapResourceName = GetType().Name + ".bmp";
base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap.");
}
}
~DynamicBikingCmd()
{
if (m_dataLoaderThread != null && m_dataLoaderThread.ThreadState == ThreadState.Running)
{
m_autoEvent.Set();
m_dataLoaderThread.Join();
}
}
#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)
{
if (hook == null)
return;
if (m_hookHelper == null)
m_hookHelper = new HookHelperClass();
m_hookHelper.Hook = hook;
m_activeView = m_hookHelper.ActiveView;
m_geometryBridge = new GeometryEnvironmentClass();
m_wksPrevPosition.X = 0;
m_wksPrevPosition.Y = 0;
}
/// <summary>
/// Occurs when this command is clicked
/// </summary>
public override void OnClick()
{
m_dynamicMap = m_hookHelper.FocusMap as IDynamicMap;
if (m_dynamicMap == null)
return;
if (!m_dynamicMap.DynamicMapEnabled)
{
MessageBox.Show("Please enable dynamic mode and try again.");
return;
}
if (!m_bConnected)
{
m_xmlPath = GetPlaybackXmlPath();
if (m_xmlPath == string.Empty)
return;
m_bikePositionInfo = new BikePositionInfo();
m_bikePositionInfo.positionCount = m_bikePositionCount;
m_bikePositionInfo.altitudeMeters = 0;
m_bikePositionInfo.time = DateTime.Now;
m_bikePositionInfo.position.X = 0;
m_bikePositionInfo.position.Y = 0;
m_bikePositionInfo.heartRate = 0;
m_bikePositionInfo.lapCount = 0;
m_bikePositionInfo.lapAverageHeartRate = 0;
m_bikePositionInfo.lapMaximumHeartRate = 0;
m_bikePositionInfo.lapDistanceMeters = 0;
m_bikePositionInfo.lapMaximumSpeed = 0;
m_bikePositionInfo.lapCalories = 0;
m_gpsPosition = new PointClass();
m_additionalInfoPoint = new PointClass();
m_additionalInfoPoint.PutCoords(70, 90);
m_bikeRouteGeometry = new PolylineClass();
// wire dynamic map events
((IDynamicMapEvents_Event)m_dynamicMap).AfterDynamicDraw += new IDynamicMapEvents_AfterDynamicDrawEventHandler(OnAfterDynamicDraw);
((IDynamicMapEvents_Event)m_dynamicMap).DynamicMapStarted += new IDynamicMapEvents_DynamicMapStartedEventHandler(OnDynamicMapStarted);
// spin the thread that plays the data from the xml file
m_dataLoaderThread = new Thread(new ParameterizedThreadStart(XmlReaderTask));
XmlDocTaksData taskData;
taskData.xmlDocPath = m_xmlPath;
m_dataLoaderThread.Start(taskData);
}
else
{
// unwire wire dynamic map events
((IDynamicMapEvents_Event)m_dynamicMap).AfterDynamicDraw -= new IDynamicMapEvents_AfterDynamicDrawEventHandler(OnAfterDynamicDraw);
((IDynamicMapEvents_Event)m_dynamicMap).DynamicMapStarted -= new IDynamicMapEvents_DynamicMapStartedEventHandler(OnDynamicMapStarted);
// force the bike xml playback thread to quite
m_autoEvent.Set();
m_dataLoaderThread.Join();
System.Diagnostics.Trace.WriteLine("Done!!!");
}
m_bConnected = !m_bConnected;
}
public override bool Checked
{
get
{
return m_bConnected;
}
}
#endregion
#region public properties
public bool IsPlaying
{
get { return m_bConnected; }
}
public bool TrackMode
{
get { return m_bTrackMode; }
set { m_bTrackMode = value; }
}
public int PlaybackSpeed
{
get { return m_playbackSpeed; }
set { m_playbackSpeed = value; }
}
#endregion
#region Private methods
private void OnAfterDynamicDraw(esriDynamicMapDrawPhase DynamicMapDrawPhase, IDisplay Display, IDynamicDisplay dynamicDisplay)
{
if (DynamicMapDrawPhase != esriDynamicMapDrawPhase.esriDMDPDynamicLayers)
return;
// initialize symbology for dynamic drawing
if (m_bOnce)
{
// create the glyphs for the bike position as well as for the route
IDynamicGlyphFactory2 dynamicGlyphFactory = dynamicDisplay.DynamicGlyphFactory as IDynamicGlyphFactory2;
IColor whiteTransparentColor = (IColor)ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(255, 255, 255));
Bitmap bitmap = new Bitmap(GetType(), "Icons.bicycle-icon.bmp");
m_bikeGlyph = dynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker, bitmap.GetHbitmap().ToInt32(), false, whiteTransparentColor);
bitmap = new Bitmap(GetType(), "Icons.cat.bmp");
m_catGlyph = dynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker, bitmap.GetHbitmap().ToInt32(), false, whiteTransparentColor);
bitmap = new Bitmap(GetType(), "Icons.gps.png");
m_gpsGlyph = dynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker, bitmap.GetHbitmap().ToInt32(), false, whiteTransparentColor);
ISymbol routeSymbol = CreateBikeRouteSymbol();
m_bikeRouteGlyph = dynamicGlyphFactory.CreateDynamicGlyph(routeSymbol);
// create the heart rate glyphs series
CreateHeartRateAnimationGlyphs(dynamicGlyphFactory);
// get the default internal text glyph
m_textGlyph = dynamicGlyphFactory.get_DynamicGlyph(1, esriDynamicGlyphType.esriDGlyphText, 1);
// do one time casting
m_dynamicSymbolProperties = dynamicDisplay as IDynamicSymbolProperties2;
m_dynamicCompoundMarker = dynamicDisplay as IDynamicCompoundMarker2;
m_dynamicScreenDisplay = dynamicDisplay as IDynamicScreenDisplay;
m_bOnce = false;
}
// draw the trail
m_dynamicSymbolProperties.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolLine, m_bikeRouteGlyph);
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolLine, 1.0f, 1.0f, 1.0f, 1.0f);
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolLine, 1.0f, 1.0f);
m_dynamicSymbolProperties.LineContinuePattern = true;
dynamicDisplay.DrawPolyline(m_bikeRouteGeometry);
if (m_playbackFormat == GPSPlaybackFormat.HST)
{
// adjust the bike lap additional info point to draw at the top left corner of the window
m_additionalInfoPoint.Y = Display.DisplayTransformation.get_DeviceFrame().bottom - 70;
// draw additional lap information
DrawLapInfo(dynamicDisplay);
// draw the heart-rate and altitude
DrawHeartRateAnimation(dynamicDisplay, m_gpsPosition);
// draw the current position as a marker glyph
m_dynamicSymbolProperties.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker, m_bikeGlyph);
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f, 1.0f, 1.0f);
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.2f, 1.2f);
m_dynamicSymbolProperties.set_RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker, esriDynamicSymbolRotationAlignment.esriDSRANorth);
m_dynamicSymbolProperties.set_Heading(esriDynamicSymbolType.esriDSymbolMarker, (float)(m_heading - 90));
dynamicDisplay.DrawMarker(m_gpsPosition);
}
else
{
DrawGPSInfo(dynamicDisplay, m_gpsPosition);
}
}
void OnDynamicMapStarted(IDisplay Display, IDynamicDisplay dynamicDisplay)
{
lock (m_bikePositionInfo)
{
// update the bike position
if (m_bikePositionInfo.positionCount != m_bikePositionCount)
{
// update the geometry
m_gpsPosition.PutCoords(m_bikePositionInfo.position.X, m_bikePositionInfo.position.Y);
// check if needed to update the map extent
if (m_bTrackMode)
{
IEnvelope mapExtent = m_hookHelper.ActiveView.ScreenDisplay.DisplayTransformation.FittedBounds;
mapExtent.CenterAt(m_gpsPosition);
m_hookHelper.ActiveView.ScreenDisplay.DisplayTransformation.VisibleBounds = mapExtent;
}
// update the bike trail
m_wksPoints[0] = m_bikePositionInfo.position;
m_geometryBridge.AddWKSPoints(m_bikeRouteGeometry, ref m_wksPoints);
// get the GPS altitude reading
m_altitudeString = string.Format("Altitude: {0} m", m_bikePositionInfo.altitudeMeters.ToString("####.#"));
if (m_playbackFormat == GPSPlaybackFormat.HST)
{
// calculate the heading
m_heading = Math.Atan2(m_bikePositionInfo.position.X - m_wksPrevPosition.X, m_bikePositionInfo.position.Y - m_wksPrevPosition.Y);
m_heading *= (180 / Math.PI);
if (m_heading < 0)
m_heading += 360;
m_heartRateString = string.Format("{0} BPM", m_bikePositionInfo.heartRate);
}
else
{
m_heading = m_bikePositionInfo.course;
m_speed = string.Format("Speed: {0} MPH", m_bikePositionInfo.speed.ToString("###.#"));
}
m_wksPrevPosition.X = m_bikePositionInfo.position.X;
m_wksPrevPosition.Y = m_bikePositionInfo.position.Y;
m_bikePositionCount = m_bikePositionInfo.positionCount;
}
}
// explicitly call refresh in order to make the dynamic display fire AfterDynamicDraw event
m_hookHelper.ActiveView.Refresh();
}
private void XmlReaderTask(object data)
{
bool bFirst = true;
DateTime nextTime = DateTime.Now;
DateTime currentTime = DateTime.Now;
double lat = 0;
double lon = 0;
double altitude = 0;
double course = 0;
double speed = 0;
int heartRate = 0;
XmlNode trackPoint = null;
XmlNode nextTrackPointTimeNode = null;
XmlDocTaksData taskData = (XmlDocTaksData)data;
XmlDocument bikeDataDoc = new XmlDocument();
XmlTextReader xmlTextReader = new XmlTextReader(m_xmlPath);
bikeDataDoc.Load(xmlTextReader);
XmlElement rootElement = bikeDataDoc.DocumentElement;
if (m_playbackFormat == GPSPlaybackFormat.HST)
{
XmlNodeList laps = rootElement.GetElementsByTagName("Lap");
foreach (XmlNode lap in laps)
{
// get lap average and maximum heart rate
XmlNode averageHeartRate = ((XmlElement)lap).GetElementsByTagName("AverageHeartRateBpm")[0];
XmlNode maximumHeartRate = ((XmlElement)lap).GetElementsByTagName("MaximumHeartRateBpm")[0];
XmlNode calories = ((XmlElement)lap).GetElementsByTagName("Calories")[0];
XmlNode maxSpeed = ((XmlElement)lap).GetElementsByTagName("MaximumSpeed")[0];
XmlNode distance = ((XmlElement)lap).GetElementsByTagName("DistanceMeters")[0];
// update the position info
lock (m_bikePositionInfo)
{
m_bikePositionInfo.lapCount++;
m_bikePositionInfo.lapAverageHeartRate = Convert.ToInt32(averageHeartRate.InnerText);
m_bikePositionInfo.lapMaximumHeartRate = Convert.ToInt32(maximumHeartRate.InnerText);
m_bikePositionInfo.lapCalories = Convert.ToInt32(calories.InnerText);
m_bikePositionInfo.lapMaximumSpeed = Convert.ToDouble(maxSpeed.InnerText);
m_bikePositionInfo.lapDistanceMeters = Convert.ToDouble(distance.InnerText);
}
XmlNodeList tracks = ((XmlElement)lap).GetElementsByTagName("Track");
foreach (XmlNode track in tracks)
{
XmlNodeList trackpoints = ((XmlElement)track).GetElementsByTagName("Trackpoint");
// read each time one track point ahead in order to calculate the lag time between
// the current track point and the next one. This time will be used as the wait time
// for the AutoResetEvent.
foreach (XmlNode nextTrackPoint in trackpoints)
{
bool bNextTime = false;
bool bTime = false;
bool bPosition = false;
bool bAltitude = false;
bool bHeartRate = false;
// if this is the first node in the first track make it current
if (bFirst)
{
trackPoint = nextTrackPoint;
bFirst = false;
continue;
}
// get the time from the next point in order to calculate the lag time
nextTrackPointTimeNode = ((XmlElement)nextTrackPoint).GetElementsByTagName("Time")[0];
if (nextTrackPointTimeNode == null)
continue;
else
{
nextTime = Convert.ToDateTime(nextTrackPointTimeNode.InnerText);
bNextTime = true;
}
if (trackPoint.ChildNodes.Count == 4)
{
foreach (XmlNode trackPointNode in trackPoint.ChildNodes)
{
if (trackPointNode.Name == "Time")
{
currentTime = Convert.ToDateTime(trackPointNode.InnerText);
bTime = true;
}
else if (trackPointNode.Name == "Position")
{
XmlNode latNode = trackPointNode["LatitudeDegrees"];
lat = Convert.ToDouble(latNode.InnerText);
XmlNode lonNode = trackPointNode["LongitudeDegrees"];
lon = Convert.ToDouble(lonNode.InnerText);
bPosition = true;
}
else if (trackPointNode.Name == "AltitudeMeters")
{
altitude = Convert.ToDouble(trackPointNode.InnerText);
bAltitude = true;
}
else if (trackPointNode.Name == "HeartRateBpm")
{
heartRate = Convert.ToInt32(trackPointNode.InnerText);
bHeartRate = true;
}
}
if (bNextTime && bTime && bPosition && bAltitude && bHeartRate)
{
TimeSpan ts = nextTime - currentTime;
lock (m_bikePositionInfo)
{
m_bikePositionInfo.position.X = lon;
m_bikePositionInfo.position.Y = lat;
m_bikePositionInfo.altitudeMeters = altitude;
m_bikePositionInfo.heartRate = heartRate;
m_bikePositionInfo.time = currentTime;
m_bikePositionInfo.positionCount++;
}
// wait for the duration of the time span or bail out if the thread was signaled
if (m_autoEvent.WaitOne((int)(ts.TotalMilliseconds / m_playbackSpeed), true))
{
return;
}
}
}
// make the next track point current
trackPoint = nextTrackPoint;
}
}
}
}
else // GPX
{
XmlNodeList trackpoints = bikeDataDoc.DocumentElement.GetElementsByTagName("trkpt");
// read each time one track point ahead in order to calculate the lag time between
// the current track point and the next one. This time will be used as the wait time
// for the AutoResetEvent.
foreach (XmlNode nextTrackPoint in trackpoints)
{
// if this is the first node in the first track make it current
if (bFirst)
{
trackPoint = nextTrackPoint;
bFirst = false;
continue;
}
// get the time from the next point in order to calculate the lag time
nextTrackPointTimeNode = ((XmlElement)nextTrackPoint).GetElementsByTagName("time")[0];
if (nextTrackPointTimeNode == null)
continue;
else
{
nextTime = Convert.ToDateTime(nextTrackPointTimeNode.InnerText);
}
lat = Convert.ToDouble(trackPoint.Attributes["lat"].InnerText);
lon = Convert.ToDouble(trackPoint.Attributes["lon"].InnerText);
foreach (XmlNode trackPointNode in trackPoint.ChildNodes)
{
if (trackPointNode.Name == "time")
{
currentTime = Convert.ToDateTime(trackPointNode.InnerText);
}
else if (trackPointNode.Name == "ele")
{
altitude = Convert.ToDouble(trackPointNode.InnerText);
}
else if (trackPointNode.Name == "course")
{
course = Convert.ToDouble(trackPointNode.InnerText);
}
else if (trackPointNode.Name == "speed")
{
speed = Convert.ToDouble(trackPointNode.InnerText);
}
}
TimeSpan ts = nextTime - currentTime;
lock (m_bikePositionInfo)
{
m_bikePositionInfo.position.X = lon;
m_bikePositionInfo.position.Y = lat;
m_bikePositionInfo.altitudeMeters = altitude;
m_bikePositionInfo.time = currentTime;
m_bikePositionInfo.course = course;
m_bikePositionInfo.speed = speed;
m_bikePositionInfo.positionCount++;
}
// wait for the duration of the time span or bail out if the thread was signaled
if (m_autoEvent.WaitOne((int)(ts.TotalMilliseconds / m_playbackSpeed), true))
{
return;
}
// make the next track point current
trackPoint = nextTrackPoint;
}
}
// close the reader when done
xmlTextReader.Close();
}
private ISymbol CreateBikeRouteSymbol()
{
IColor color = (IColor)ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(0, 90, 250));
ICharacterMarkerSymbol charMarkerSymbol = new CharacterMarkerSymbolClass();
charMarkerSymbol.Color = color;
charMarkerSymbol.Font = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToStdFont(new Font("ESRI Default Marker", 17.0f, FontStyle.Bold));
charMarkerSymbol.CharacterIndex = 189;
charMarkerSymbol.Size = 17;
IMarkerLineSymbol markerLineSymbol = new MarkerLineSymbolClass();
markerLineSymbol.Color = color;
markerLineSymbol.Width = 17.0;
markerLineSymbol.MarkerSymbol = (IMarkerSymbol)charMarkerSymbol;
// Makes a new Cartographic Line symbol and sets its properties
ICartographicLineSymbol cartographicLineSymbol = markerLineSymbol as ICartographicLineSymbol;
// In order to set additional properties like offsets and dash patterns we must create an ILineProperties object
ILineProperties lineProperties = cartographicLineSymbol as ILineProperties;
lineProperties.Offset = 0;
// Here's how to do a template for the pattern of marks and gaps
double[] hpe = new double[4];
hpe[0] = 0;
hpe[1] = 39;
hpe[2] = 1;
hpe[3] = 0;
ITemplate template = new TemplateClass();
template.Interval = 1;
for (int i = 0; i < hpe.Length; i = i + 2)
{
template.AddPatternElement(hpe[i], hpe[i + 1]);
}
lineProperties.Template = template;
// Set the basic and cartographic line properties
cartographicLineSymbol.Color = color;
color = (IColor)ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(0, 220, 100));
// create a simple line
ISimpleLineSymbol simpleLineSymbol = new SimpleLineSymbolClass();
simpleLineSymbol.Color = color;
simpleLineSymbol.Style = esriSimpleLineStyle.esriSLSSolid;
simpleLineSymbol.Width = 1.2;
IMultiLayerLineSymbol multiLayerLineSymbol = new MultiLayerLineSymbolClass();
multiLayerLineSymbol.AddLayer((ILineSymbol)cartographicLineSymbol);
multiLayerLineSymbol.AddLayer((ILineSymbol)simpleLineSymbol);
return multiLayerLineSymbol as ISymbol;
}
private void CreateHeartRateAnimationGlyphs(IDynamicGlyphFactory2 dynamicGlyphFactory)
{
IColor whiteTransparentColor = (IColor)ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(255, 255, 255));
m_heartRateGlyph = new IDynamicGlyph[5];
string heartRateIcon;
string heartIconBaseName = "Icons.valentine-heart";
Bitmap bitmap = null;
int imagesize = 16;
for (int i = 0; i < 5; i++)
{
heartRateIcon = heartIconBaseName + imagesize + ".bmp";
bitmap = new Bitmap(GetType(), heartRateIcon);
m_heartRateGlyph[i] = dynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker, bitmap.GetHbitmap().ToInt32(), false, whiteTransparentColor);
m_heartRateGlyph[i].SetAnchor(20.0f, -40.0f);
imagesize += 2;
}
}
private void DrawHeartRateAnimation(IDynamicDisplay dynamicDisplay, IPoint bikePoint)
{
m_dynamicSymbolProperties.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker, m_heartRateGlyph[m_heartRateCounter]);
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f, 1.0f, 1.0f);
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f);
m_dynamicSymbolProperties.set_RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker, esriDynamicSymbolRotationAlignment.esriDSRAScreen);
m_dynamicSymbolProperties.set_Heading(esriDynamicSymbolType.esriDSymbolMarker, 0.0f);
dynamicDisplay.DrawMarker(bikePoint);
m_textGlyph.SetAnchor(-35.0f, -50.0f);
m_dynamicSymbolProperties.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolText, m_textGlyph);
m_dynamicSymbolProperties.TextBoxUseDynamicFillSymbol = true;
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolText, 0.0f, 0.8f, 0.0f, 1.0f);
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolFill, 0.0f, 0.0f, 0.0f, 1.0f);
m_dynamicSymbolProperties.TextBoxHorizontalAlignment = esriTextHorizontalAlignment.esriTHALeft;
dynamicDisplay.DrawText(bikePoint, m_heartRateString);
m_textGlyph.SetAnchor(-20.0f, -30.0f);
m_dynamicSymbolProperties.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolText, m_textGlyph);
dynamicDisplay.DrawText(bikePoint, m_altitudeString);
if (m_drawCycles % 5 == 0)
{
m_heartRateCounter++;
if (m_heartRateCounter > 4)
m_heartRateCounter = 0;
}
m_drawCycles++;
if (m_drawCycles == 5)
m_drawCycles = 0;
}
private void DrawGPSInfo(IDynamicDisplay dynamicDisplay, IPoint gpsPosition)
{
// altitude is already available
string course;
string speed;
lock (m_bikePositionInfo)
{
course = string.Format("Course {0} DEG", m_bikePositionInfo.course.ToString("###.##"));
speed = string.Format("Speed {0} MPH", m_bikePositionInfo.speed.ToString("###.##"));
}
string gpsInfo = string.Format("{0}\n{1}\n{2}", course, speed, m_altitudeString);
m_textGlyph.SetAnchor(-35.0f, -47.0f);
m_dynamicSymbolProperties.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolText, m_textGlyph);
m_dynamicSymbolProperties.TextBoxUseDynamicFillSymbol = true;
m_dynamicSymbolProperties.set_Heading(esriDynamicSymbolType.esriDSymbolText, 0.0f);
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolText, 0.0f, 0.8f, 0.0f, 1.0f);
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolText, 1.0f, 1.0f);
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolFill, 0.0f, 0.0f, 0.0f, 0.6f);
m_dynamicSymbolProperties.TextBoxHorizontalAlignment = esriTextHorizontalAlignment.esriTHALeft;
dynamicDisplay.DrawText(m_gpsPosition, gpsInfo);
m_dynamicSymbolProperties.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker, m_gpsGlyph);
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f, 1.0f, 1.0f);
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolMarker, m_gpsSymbolScale, m_gpsSymbolScale);
m_dynamicSymbolProperties.set_RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker, esriDynamicSymbolRotationAlignment.esriDSRANorth);
m_dynamicSymbolProperties.set_Heading(esriDynamicSymbolType.esriDSymbolMarker, (float)(m_heading - 90));
dynamicDisplay.DrawMarker(m_gpsPosition);
if (m_drawCycles % 5 == 0)
{
// increment the symbol size
m_gpsSymbolScale += 0.05f;
if (m_gpsSymbolScale > 1.2f)
m_gpsSymbolScale = 0.8f;
}
m_drawCycles++;
if (m_drawCycles == 5)
m_drawCycles = 0;
}
private void DrawLapInfo(IDynamicDisplay dynamicDisplay)
{
string lapCount;
string lapInfo;
string lapHeartRateInfo;
lock (m_bikePositionInfo)
{
lapCount = string.Format("Lap #{0}", m_bikePositionInfo.lapCount);
lapInfo = string.Format("Lap information:\nDistance: {0}m\nMaximum speed - {1}\nCalories - {2}", m_bikePositionInfo.lapDistanceMeters.ToString("#####.#"), m_bikePositionInfo.lapMaximumSpeed.ToString("###.#"), m_bikePositionInfo.lapCalories);
lapHeartRateInfo = string.Format("Lap heart rate info:\nAverage - {0}\nMaximum - {1}", m_bikePositionInfo.lapAverageHeartRate, m_bikePositionInfo.lapMaximumHeartRate);
}
m_dynamicSymbolProperties.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker, m_catGlyph);
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f, 1.0f, 1.0f);
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f);
m_dynamicSymbolProperties.set_RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker, esriDynamicSymbolRotationAlignment.esriDSRAScreen);
m_dynamicSymbolProperties.set_Heading(esriDynamicSymbolType.esriDSymbolMarker, 0.0f);
m_dynamicSymbolProperties.TextBoxUseDynamicFillSymbol = true;
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolText, 0.0f, 0.8f, 0.0f, 1.0f);
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolFill, 0.0f, 0.0f, 0.0f, 1.0f);
m_dynamicSymbolProperties.TextBoxHorizontalAlignment = esriTextHorizontalAlignment.esriTHALeft;
m_textGlyph.SetAnchor(0.0f, 0.0f);
m_dynamicSymbolProperties.set_DynamicGlyph(esriDynamicSymbolType.esriDSymbolText, m_textGlyph);
string[] strLapInfo = new string[] { lapCount, lapInfo, lapHeartRateInfo };
m_dynamicCompoundMarker.DrawScreenArrayMarker(m_additionalInfoPoint, ref nullString, ref nullString, ref strLapInfo, ref nullString, ref nullString);
}
private string GetPlaybackXmlPath()
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.CheckFileExists = true;
ofd.Multiselect = false;
ofd.Filter = "HST files (*.hst)|*.hst|GPX files (*.gpx)|*.gpx|XML files (*.xml)|*.xml";
ofd.RestoreDirectory = true;
ofd.Title = "Input biking log file";
DialogResult result = ofd.ShowDialog();
if (result == DialogResult.OK)
{
string fileExtension = System.IO.Path.GetExtension(ofd.FileName).ToUpper();
if (fileExtension == ".GPX")
m_playbackFormat = GPSPlaybackFormat.GPX;
else if (fileExtension == ".HST")
m_playbackFormat = GPSPlaybackFormat.HST;
else if (fileExtension == ".XML")
m_playbackFormat = GPSPlaybackFormat.XML;
return ofd.FileName;
}
return string.Empty;
}
#endregion
}
}
[Visual Basic .NET]
DynamicBikingCmd.vb
Imports Microsoft.VisualBasic
Imports System
Imports System.IO
Imports System.Drawing
Imports System.Xml
Imports System.Xml.XPath
Imports System.Threading
Imports System.Windows.Forms
Imports System.Collections.Generic
Imports System.Runtime.InteropServices
Imports ESRI.ArcGIS.ADF.BaseClasses
Imports ESRI.ArcGIS.ADF.CATIDs
Imports ESRI.ArcGIS.Controls
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.Display
Imports ESRI.ArcGIS.esriSystem
''' <summary>
''' Summary description for DynamicBikingCmd.
''' </summary>
<Guid("f01054d2-0130-4124-8436-1bf2942bf2b6"), ClassInterface(ClassInterfaceType.None), ProgId("DynamicBikingCmd")> _
Public NotInheritable Class DynamicBikingCmd : Inherits BaseCommand
#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)
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)
ControlsCommands.Unregister(regKey)
End Sub
#End Region
#End Region
#Region "class members"
Private Enum GPSPlaybackFormat
HST = 0
GPX = 1
XML = 2
End Enum
Private m_playbackFormat As GPSPlaybackFormat = GPSPlaybackFormat.GPX
Private m_hookHelper As IHookHelper
Private m_dynamicMap As IDynamicMap = Nothing
Private m_activeView As IActiveView = Nothing
Private m_bConnected As Boolean = False
Private m_gpsPosition As IPoint = Nothing
Private m_additionalInfoPoint As IPoint = Nothing
Private m_bikeRouteGeometry As IPointCollection4 = Nothing
Private m_geometryBridge As IGeometryBridge2 = Nothing
Private m_wksPoints As WKSPoint() = New WKSPoint(0){}
Private m_wksPrevPosition As WKSPoint
Private m_dynamicSymbolProperties As IDynamicSymbolProperties2 = Nothing
Private m_dynamicCompoundMarker As IDynamicCompoundMarker2 = Nothing
Private m_dynamicScreenDisplay As IDynamicScreenDisplay = Nothing
Private m_bikeGlyph As IDynamicGlyph = Nothing
Private m_bikeRouteGlyph As IDynamicGlyph = Nothing
Private m_textGlyph As IDynamicGlyph = Nothing
Private m_catGlyph As IDynamicGlyph = Nothing
Private m_gpsGlyph As IDynamicGlyph = Nothing
Private m_heartRateGlyph As IDynamicGlyph()
Private m_gpsSymbolScale As Single = 1.0f
Private m_heading As Double = 0
Private m_heartRateString As String = String.Empty
Private m_altitudeString As String = String.Empty
Private m_speed As String = String.Empty
Private m_bOnce As Boolean = True
Private m_heartRateCounter As Integer = 0
Private m_drawCycles As Integer = 0
Public m_playbackSpeed As Integer = 10
Private m_bTrackMode As Boolean = False
Private nullString As String() = Nothing
Private m_xmlPath As String = String.Empty
' xml loader thread stuff
Private m_dataLoaderThread As Thread = Nothing
Private Shared m_autoEvent As AutoResetEvent = New AutoResetEvent(False)
Private m_bikePositionCount As Integer = 0
Private NotInheritable Class BikePositionInfo
Public Sub New()
End Sub
Public position As WKSPoint
Public time As DateTime
Public altitudeMeters As Double
Public heartRate As Integer
Public lapCount As Integer
Public lapAverageHeartRate As Integer
Public lapMaximumHeartRate As Integer
Public lapCalories As Integer
Public lapMaximumSpeed As Double
Public lapDistanceMeters As Double
Public course As Double
Public speed As Double
Public positionCount As Integer
End Class
Private m_bikePositionInfo As BikePositionInfo = Nothing
Private Structure XmlDocTaksData
Public xmlDocPath As String
End Structure
#End Region
#Region "class constructor"
Public Sub New()
MyBase.m_category = ".NET Samples"
MyBase.m_caption = "Dynamic Biking"
MyBase.m_message = "Dynamic Biking"
MyBase.m_toolTip = "Dynamic Biking"
MyBase.m_name = "DynamicBikingCmd"
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
Protected Overrides Sub Finalize()
If Not m_dataLoaderThread Is Nothing AndAlso m_dataLoaderThread.ThreadState = ThreadState.Running Then
m_autoEvent.Set()
m_dataLoaderThread.Join()
End If
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)
If hook Is Nothing Then
Return
End If
If m_hookHelper Is Nothing Then
m_hookHelper = New HookHelperClass()
End If
m_hookHelper.Hook = hook
m_activeView = m_hookHelper.ActiveView
m_geometryBridge = New GeometryEnvironmentClass()
m_wksPrevPosition.X = 0
m_wksPrevPosition.Y = 0
End Sub
''' <summary>
''' Occurs when this command is clicked
''' </summary>
Public Overrides Sub OnClick()
m_dynamicMap = TryCast(m_hookHelper.FocusMap, IDynamicMap)
If m_dynamicMap Is Nothing Then
Return
End If
If (Not m_dynamicMap.DynamicMapEnabled) Then
MessageBox.Show("Please enable dynamic mode and try again.")
Return
End If
If (Not m_bConnected) Then
m_xmlPath = GetPlaybackXmlPath()
If m_xmlPath = String.Empty Then
Return
End If
m_bikePositionInfo = New BikePositionInfo()
m_bikePositionInfo.positionCount = m_bikePositionCount
m_bikePositionInfo.altitudeMeters = 0
m_bikePositionInfo.time = DateTime.Now
m_bikePositionInfo.position.X = 0
m_bikePositionInfo.position.Y = 0
m_bikePositionInfo.heartRate = 0
m_bikePositionInfo.lapCount = 0
m_bikePositionInfo.lapAverageHeartRate = 0
m_bikePositionInfo.lapMaximumHeartRate = 0
m_bikePositionInfo.lapDistanceMeters = 0
m_bikePositionInfo.lapMaximumSpeed = 0
m_bikePositionInfo.lapCalories = 0
m_gpsPosition = New PointClass()
m_additionalInfoPoint = New PointClass()
m_additionalInfoPoint.PutCoords(70, 90)
m_bikeRouteGeometry = New PolylineClass()
' wire dynamic map events
AddHandler (CType(m_dynamicMap, IDynamicMapEvents_Event)).AfterDynamicDraw, AddressOf OnAfterDynamicDraw
AddHandler (CType(m_dynamicMap, IDynamicMapEvents_Event)).DynamicMapStarted, AddressOf OnDynamicMapStarted
' spin the thread that plays the data from the xml file
m_dataLoaderThread = New Thread(New ParameterizedThreadStart(AddressOf XmlReaderTask))
Dim taskData As XmlDocTaksData
taskData.xmlDocPath = m_xmlPath
m_dataLoaderThread.Start(taskData)
Else
' unwire wire dynamic map events
RemoveHandler (CType(m_dynamicMap, IDynamicMapEvents_Event)).AfterDynamicDraw, AddressOf OnAfterDynamicDraw
RemoveHandler (CType(m_dynamicMap, IDynamicMapEvents_Event)).DynamicMapStarted, AddressOf OnDynamicMapStarted
' force the bike xml playback thread to quite
m_autoEvent.Set()
m_dataLoaderThread.Join()
System.Diagnostics.Trace.WriteLine("Done!!!")
End If
m_bConnected = Not m_bConnected
End Sub
Public Overrides ReadOnly Property Checked() As Boolean
Get
Return m_bConnected
End Get
End Property
#End Region
#Region "public properties"
Public ReadOnly Property IsPlaying() As Boolean
Get
Return m_bConnected
End Get
End Property
Public Property TrackMode() As Boolean
Get
Return m_bTrackMode
End Get
Set
m_bTrackMode = Value
End Set
End Property
Public Property PlaybackSpeed() As Integer
Get
Return m_playbackSpeed
End Get
Set
m_playbackSpeed = Value
End Set
End Property
#End Region
#Region "Private methods"
Private Sub OnAfterDynamicDraw(ByVal DynamicMapDrawPhase As esriDynamicMapDrawPhase, ByVal Display As IDisplay, ByVal dynamicDisplay As IDynamicDisplay)
if (DynamicMapDrawPhase <> esriDynamicMapDrawPhase.esriDMDPDynamicLayers) then
return
End if
' initialize symbology for dynamic drawing
If m_bOnce Then
' create the glyphs for the bike position as well as for the route
Dim dynamicGlyphFactory As IDynamicGlyphFactory2 = TryCast(dynamicDisplay.DynamicGlyphFactory, IDynamicGlyphFactory2)
Dim whiteTransparentColor As IColor = CType(ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(255, 255, 255)), IColor)
Dim bitmap As Bitmap = New Bitmap(Me.GetType(), "bicycle-icon.bmp")
m_bikeGlyph = dynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker, bitmap.GetHbitmap().ToInt32(), False, whiteTransparentColor)
bitmap = New Bitmap(Me.GetType(), "cat.bmp")
m_catGlyph = dynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker, bitmap.GetHbitmap().ToInt32(), False, whiteTransparentColor)
bitmap = New Bitmap(Me.GetType(), "gps.png")
m_gpsGlyph = dynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker, bitmap.GetHbitmap().ToInt32(), False, whiteTransparentColor)
Dim routeSymbol As ISymbol = CreateBikeRouteSymbol()
m_bikeRouteGlyph = dynamicGlyphFactory.CreateDynamicGlyph(routeSymbol)
' create the heart rate glyphs series
CreateHeartRateAnimationGlyphs(dynamicGlyphFactory)
' get the default internal text glyph
m_textGlyph = dynamicGlyphFactory.DynamicGlyph(1, esriDynamicGlyphType.esriDGlyphText, 1)
' do one time casting
m_dynamicSymbolProperties = TryCast(dynamicDisplay, IDynamicSymbolProperties2)
m_dynamicCompoundMarker = TryCast(dynamicDisplay, IDynamicCompoundMarker2)
m_dynamicScreenDisplay = TryCast(dynamicDisplay, IDynamicScreenDisplay)
m_bOnce = False
End If
' draw the trail
m_dynamicSymbolProperties.DynamicGlyph(esriDynamicSymbolType.esriDSymbolLine)= m_bikeRouteGlyph
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolLine, 1.0f, 1.0f, 1.0f, 1.0f)
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolLine, 1.0f, 1.0f)
m_dynamicSymbolProperties.LineContinuePattern = True
dynamicDisplay.DrawPolyline(m_bikeRouteGeometry)
If m_playbackFormat = GPSPlaybackFormat.HST Then
' adjust the bike lap additional info point to draw at the top left corner of the window
m_additionalInfoPoint.Y = Display.DisplayTransformation.DeviceFrame().bottom - 70
' draw additional lap information
DrawLapInfo(dynamicDisplay)
' draw the heart-rate and altitude
DrawHeartRateAnimation(dynamicDisplay, m_gpsPosition)
' draw the current position as a marker glyph
m_dynamicSymbolProperties.DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker) = m_bikeGlyph
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0F, 1.0F, 1.0F, 1.0F)
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.2F, 1.2F)
m_dynamicSymbolProperties.RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker) = esriDynamicSymbolRotationAlignment.esriDSRANorth
m_dynamicSymbolProperties.Heading(esriDynamicSymbolType.esriDSymbolMarker) = CSng(m_heading - 90)
dynamicDisplay.DrawMarker(m_gpsPosition)
Else
DrawGPSInfo(dynamicDisplay, m_gpsPosition)
End If
End Sub
Private Sub OnDynamicMapStarted(ByVal Display As IDisplay, ByVal dynamicDisplay As IDynamicDisplay)
SyncLock m_bikePositionInfo
' update the bike position
If m_bikePositionInfo.positionCount <> m_bikePositionCount Then
' update the geometry
m_gpsPosition.PutCoords(m_bikePositionInfo.position.X, m_bikePositionInfo.position.Y)
' check if needed to update the map extent
If m_bTrackMode Then
Dim mapExtent As IEnvelope = m_hookHelper.ActiveView.ScreenDisplay.DisplayTransformation.FittedBounds
mapExtent.CenterAt(m_gpsPosition)
m_hookHelper.ActiveView.ScreenDisplay.DisplayTransformation.VisibleBounds = mapExtent
End If
' update the bike trail
m_wksPoints(0) = m_bikePositionInfo.position
m_geometryBridge.AddWKSPoints(m_bikeRouteGeometry, m_wksPoints)
' get the GPS altitude reading
m_altitudeString = String.Format("Altitude: {0} m", m_bikePositionInfo.altitudeMeters.ToString("####.#"))
If m_playbackFormat = GPSPlaybackFormat.HST Then
' calculate the heading
m_heading = Math.Atan2(m_bikePositionInfo.position.X - m_wksPrevPosition.X, m_bikePositionInfo.position.Y - m_wksPrevPosition.Y)
m_heading *= (180 / Math.PI)
If m_heading < 0 Then
m_heading += 360
End If
m_heartRateString = String.Format("{0} BPM", m_bikePositionInfo.heartRate)
Else
m_heading = m_bikePositionInfo.course
m_speed = String.Format("Speed: {0} MPH", m_bikePositionInfo.speed.ToString("###.#"))
End If
m_wksPrevPosition.X = m_bikePositionInfo.position.X
m_wksPrevPosition.Y = m_bikePositionInfo.position.Y
m_bikePositionCount = m_bikePositionInfo.positionCount
End If
End SyncLock
' explicitly call refresh in order to make the dynamic display fire AfterDynamicDraw event
m_hookHelper.ActiveView.Refresh()
End Sub
Private Sub XmlReaderTask(ByVal data As Object)
Dim bFirst As Boolean = True
Dim nextTime As DateTime = DateTime.Now
Dim currentTime As DateTime = DateTime.Now
Dim lat As Double = 0
Dim lon As Double = 0
Dim altitude As Double = 0
Dim course As Double = 0
Dim speed As Double = 0
Dim heartRate As Integer = 0
Dim trackPoint As XmlNode = Nothing
Dim nextTrackPointTimeNode As XmlNode = Nothing
Dim taskData As XmlDocTaksData = CType(data, XmlDocTaksData)
Dim bikeDataDoc As XmlDocument = New XmlDocument()
Dim xmlTextReader As XmlTextReader = New XmlTextReader(m_xmlPath)
bikeDataDoc.Load(xmlTextReader)
Dim rootElement As XmlElement = bikeDataDoc.DocumentElement
If m_playbackFormat = GPSPlaybackFormat.HST Then
Dim laps As XmlNodeList = rootElement.GetElementsByTagName("Lap")
For Each lap As XmlNode In laps
' get lap average and maximum heart rate
Dim averageHeartRate As XmlNode = (CType(lap, XmlElement)).GetElementsByTagName("AverageHeartRateBpm")(0)
Dim maximumHeartRate As XmlNode = (CType(lap, XmlElement)).GetElementsByTagName("MaximumHeartRateBpm")(0)
Dim calories As XmlNode = (CType(lap, XmlElement)).GetElementsByTagName("Calories")(0)
Dim maxSpeed As XmlNode = (CType(lap, XmlElement)).GetElementsByTagName("MaximumSpeed")(0)
Dim distance As XmlNode = (CType(lap, XmlElement)).GetElementsByTagName("DistanceMeters")(0)
' update the position info
SyncLock m_bikePositionInfo
m_bikePositionInfo.lapCount += 1
m_bikePositionInfo.lapAverageHeartRate = Convert.ToInt32(averageHeartRate.InnerText)
m_bikePositionInfo.lapMaximumHeartRate = Convert.ToInt32(maximumHeartRate.InnerText)
m_bikePositionInfo.lapCalories = Convert.ToInt32(calories.InnerText)
m_bikePositionInfo.lapMaximumSpeed = Convert.ToDouble(maxSpeed.InnerText)
m_bikePositionInfo.lapDistanceMeters = Convert.ToDouble(distance.InnerText)
End SyncLock
Dim tracks As XmlNodeList = (CType(lap, XmlElement)).GetElementsByTagName("Track")
For Each track As XmlNode In tracks
Dim trackpoints As XmlNodeList = (CType(track, XmlElement)).GetElementsByTagName("Trackpoint")
' read each time one track point ahead in order to calculate the lag time between
' the current track point and the next one. This time will be used as the wait time
' for the AutoResetEvent.
For Each nextTrackPoint As XmlNode In trackpoints
Dim bNextTime As Boolean = False
Dim bTime As Boolean = False
Dim bPosition As Boolean = False
Dim bAltitude As Boolean = False
Dim bHeartRate As Boolean = False
' if this is the first node in the first track make it current
If bFirst Then
trackPoint = nextTrackPoint
bFirst = False
Continue For
End If
' get the time from the next point in order to calculate the lag time
nextTrackPointTimeNode = (CType(nextTrackPoint, XmlElement)).GetElementsByTagName("Time")(0)
If nextTrackPointTimeNode Is Nothing Then
Continue For
Else
nextTime = Convert.ToDateTime(nextTrackPointTimeNode.InnerText)
bNextTime = True
End If
If trackPoint.ChildNodes.Count = 4 Then
For Each trackPointNode As XmlNode In trackPoint.ChildNodes
If trackPointNode.Name = "Time" Then
currentTime = Convert.ToDateTime(trackPointNode.InnerText)
bTime = True
ElseIf trackPointNode.Name = "Position" Then
Dim latNode As XmlNode = trackPointNode("LatitudeDegrees")
lat = Convert.ToDouble(latNode.InnerText)
Dim lonNode As XmlNode = trackPointNode("LongitudeDegrees")
lon = Convert.ToDouble(lonNode.InnerText)
bPosition = True
ElseIf trackPointNode.Name = "AltitudeMeters" Then
altitude = Convert.ToDouble(trackPointNode.InnerText)
bAltitude = True
ElseIf trackPointNode.Name = "HeartRateBpm" Then
heartRate = Convert.ToInt32(trackPointNode.InnerText)
bHeartRate = True
End If
Next trackPointNode
If bNextTime AndAlso bTime AndAlso bPosition AndAlso bAltitude AndAlso bHeartRate Then
Dim ts As TimeSpan = nextTime.Subtract(currentTime)
SyncLock m_bikePositionInfo
m_bikePositionInfo.position.X = lon
m_bikePositionInfo.position.Y = lat
m_bikePositionInfo.altitudeMeters = altitude
m_bikePositionInfo.heartRate = heartRate
m_bikePositionInfo.time = currentTime
m_bikePositionInfo.positionCount += 1
End SyncLock
' wait for the duration of the time span or bail out if the thread was signaled
If m_autoEvent.WaitOne(CInt(ts.TotalMilliseconds / m_playbackSpeed), True) Then
Return
End If
End If
End If
' make the next track point current
trackPoint = nextTrackPoint
Next nextTrackPoint
Next track
Next lap
Else ' GPX
Dim trackpoints As XmlNodeList = bikeDataDoc.DocumentElement.GetElementsByTagName("trkpt")
' read each time one track point ahead in order to calculate the lag time between
' the current track point and the next one. This time will be used as the wait time
' for the AutoResetEvent.
For Each nextTrackPoint As XmlNode In trackpoints
' if this is the first node in the first track make it current
If bFirst Then
trackPoint = nextTrackPoint
bFirst = False
Continue For
End If
' get the time from the next point in order to calculate the lag time
nextTrackPointTimeNode = (CType(nextTrackPoint, XmlElement)).GetElementsByTagName("time")(0)
If nextTrackPointTimeNode Is Nothing Then
Continue For
Else
nextTime = Convert.ToDateTime(nextTrackPointTimeNode.InnerText)
End If
lat = Convert.ToDouble(trackPoint.Attributes("lat").InnerText)
lon = Convert.ToDouble(trackPoint.Attributes("lon").InnerText)
For Each trackPointNode As XmlNode In trackPoint.ChildNodes
If trackPointNode.Name = "time" Then
currentTime = Convert.ToDateTime(trackPointNode.InnerText)
ElseIf trackPointNode.Name = "ele" Then
altitude = Convert.ToDouble(trackPointNode.InnerText)
ElseIf trackPointNode.Name = "course" Then
course = Convert.ToDouble(trackPointNode.InnerText)
ElseIf trackPointNode.Name = "speed" Then
speed = Convert.ToDouble(trackPointNode.InnerText)
End If
Next trackPointNode
Dim ts As TimeSpan = nextTime.Subtract(currentTime)
SyncLock m_bikePositionInfo
m_bikePositionInfo.position.X = lon
m_bikePositionInfo.position.Y = lat
m_bikePositionInfo.altitudeMeters = altitude
m_bikePositionInfo.time = currentTime
m_bikePositionInfo.course = course
m_bikePositionInfo.speed = speed
m_bikePositionInfo.positionCount += 1
End SyncLock
' wait for the duration of the time span or bail out if the thread was signaled
If m_autoEvent.WaitOne(CInt(ts.TotalMilliseconds / m_playbackSpeed), True) Then
Return
End If
' make the next track point current
trackPoint = nextTrackPoint
Next nextTrackPoint
End If
' close the reader when done
xmlTextReader.Close()
End Sub
Private Function CreateBikeRouteSymbol() As ISymbol
Dim newColor As IColor = CType(ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(0, 90, 250)), IColor)
Dim charMarkerSymbol As ICharacterMarkerSymbol = New CharacterMarkerSymbolClass()
charMarkerSymbol.Color = newColor
charMarkerSymbol.Font = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToStdFont(New Font("ESRI Default Marker", 17.0F, FontStyle.Bold))
charMarkerSymbol.CharacterIndex = 189
charMarkerSymbol.Size = 17
Dim markerLineSymbol As IMarkerLineSymbol = New MarkerLineSymbolClass()
markerLineSymbol.Color = newColor
markerLineSymbol.Width = 17.0
markerLineSymbol.MarkerSymbol = CType(charMarkerSymbol, IMarkerSymbol)
' Makes a new Cartographic Line symbol and sets its properties
Dim cartographicLineSymbol As ICartographicLineSymbol = TryCast(markerLineSymbol, ICartographicLineSymbol)
' In order to set additional properties like offsets and dash patterns we must create an ILineProperties object
Dim lineProperties As ILineProperties = TryCast(cartographicLineSymbol, ILineProperties)
lineProperties.Offset = 0
' Here's how to do a template for the pattern of marks and gaps
Dim hpe As Double() = New Double(3){}
hpe(0) = 0
hpe(1) = 39
hpe(2) = 1
hpe(3) = 0
Dim template As ITemplate = New TemplateClass()
template.Interval = 1
Dim i As Integer = 0
Do While i < hpe.Length
template.AddPatternElement(hpe(i), hpe(i + 1))
i = i + 2
Loop
lineProperties.Template = template
' Set the basic and cartographic line properties
cartographicLineSymbol.Color = newColor
newColor = CType(ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(0, 220, 100)), IColor)
' create a simple line
Dim simpleLineSymbol As ISimpleLineSymbol = New SimpleLineSymbolClass()
simpleLineSymbol.Color = newColor
simpleLineSymbol.Style = esriSimpleLineStyle.esriSLSSolid
simpleLineSymbol.Width = 1.2
Dim multiLayerLineSymbol As IMultiLayerLineSymbol = New MultiLayerLineSymbolClass()
multiLayerLineSymbol.AddLayer(CType(cartographicLineSymbol, ILineSymbol))
multiLayerLineSymbol.AddLayer(CType(simpleLineSymbol, ILineSymbol))
Return TryCast(multiLayerLineSymbol, ISymbol)
End Function
Private Sub CreateHeartRateAnimationGlyphs(ByVal dynamicGlyphFactory As IDynamicGlyphFactory2)
Dim whiteTransparentColor As IColor = CType(ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.FromArgb(255, 255, 255)), IColor)
m_heartRateGlyph = New IDynamicGlyph(4){}
Dim heartRateIcon As String
Dim heartIconBaseName As String = "valentine-heart"
Dim bitmap As Bitmap = Nothing
Dim imagesize As Integer = 16
For i As Integer = 0 To 4
heartRateIcon = heartIconBaseName & imagesize & ".bmp"
bitmap = New Bitmap(Me.GetType(), heartRateIcon)
m_heartRateGlyph(i) = dynamicGlyphFactory.CreateDynamicGlyphFromBitmap(esriDynamicGlyphType.esriDGlyphMarker, bitmap.GetHbitmap().ToInt32(), False, whiteTransparentColor)
m_heartRateGlyph(i).SetAnchor(20.0F, -40.0F)
imagesize += 2
Next i
End Sub
Private Sub DrawHeartRateAnimation(ByVal dynamicDisplay As IDynamicDisplay, ByVal bikePoint As IPoint)
m_dynamicSymbolProperties.DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker)= m_heartRateGlyph(m_heartRateCounter)
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f, 1.0f, 1.0f)
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f)
m_dynamicSymbolProperties.RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker)= esriDynamicSymbolRotationAlignment.esriDSRAScreen
m_dynamicSymbolProperties.Heading(esriDynamicSymbolType.esriDSymbolMarker)= 0.0f
dynamicDisplay.DrawMarker(bikePoint)
m_textGlyph.SetAnchor(-35.0f, -50.0f)
m_dynamicSymbolProperties.DynamicGlyph(esriDynamicSymbolType.esriDSymbolText)= m_textGlyph
m_dynamicSymbolProperties.TextBoxUseDynamicFillSymbol = True
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolText, 0.0f, 0.8f, 0.0f, 1.0f)
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolFill, 0.0f, 0.0f, 0.0f, 1.0f)
m_dynamicSymbolProperties.TextBoxHorizontalAlignment = esriTextHorizontalAlignment.esriTHALeft
dynamicDisplay.DrawText(bikePoint, m_heartRateString)
m_textGlyph.SetAnchor(-20.0f, -30.0f)
m_dynamicSymbolProperties.DynamicGlyph(esriDynamicSymbolType.esriDSymbolText)= m_textGlyph
dynamicDisplay.DrawText(bikePoint, m_altitudeString)
If m_drawCycles Mod 5 = 0 Then
m_heartRateCounter += 1
If m_heartRateCounter > 4 Then
m_heartRateCounter = 0
End If
End If
m_drawCycles += 1
If m_drawCycles = 5 Then
m_drawCycles = 0
End If
End Sub
Private Sub DrawGPSInfo(ByVal dynamicDisplay As IDynamicDisplay, ByVal gpsPosition As IPoint)
' altitude is already available
Dim course As String
Dim speed As String
SyncLock m_bikePositionInfo
course = String.Format("Course {0} DEG", m_bikePositionInfo.course.ToString("###.##"))
speed = String.Format("Speed {0} MPH", m_bikePositionInfo.speed.ToString("###.##"))
End SyncLock
Dim gpsInfo As String = String.Format("{0}" & Constants.vbLf & "{1}" & Constants.vbLf & "{2}", course, speed, m_altitudeString)
m_textGlyph.SetAnchor(-35.0F, -47.0F)
m_dynamicSymbolProperties.DynamicGlyph(esriDynamicSymbolType.esriDSymbolText) = m_textGlyph
m_dynamicSymbolProperties.TextBoxUseDynamicFillSymbol = True
m_dynamicSymbolProperties.Heading(esriDynamicSymbolType.esriDSymbolText) = 0.0F
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolText, 0.0F, 0.8F, 0.0F, 1.0F)
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolText, 1.0F, 1.0F)
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolFill, 0.0F, 0.0F, 0.0F, 0.6F)
m_dynamicSymbolProperties.TextBoxHorizontalAlignment = esriTextHorizontalAlignment.esriTHALeft
dynamicDisplay.DrawText(m_gpsPosition, gpsInfo)
m_dynamicSymbolProperties.DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker) = m_gpsGlyph
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0F, 1.0F, 1.0F, 1.0F)
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolMarker, m_gpsSymbolScale, m_gpsSymbolScale)
m_dynamicSymbolProperties.RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker) = esriDynamicSymbolRotationAlignment.esriDSRANorth
m_dynamicSymbolProperties.Heading(esriDynamicSymbolType.esriDSymbolMarker) = CSng(m_heading - 90)
dynamicDisplay.DrawMarker(m_gpsPosition)
If m_drawCycles Mod 5 = 0 Then
' increment the symbol size
m_gpsSymbolScale += 0.05F
If m_gpsSymbolScale > 1.2F Then
m_gpsSymbolScale = 0.8F
End If
End If
m_drawCycles += 1
If m_drawCycles = 5 Then
m_drawCycles = 0
End If
End Sub
Private Sub DrawLapInfo(ByVal dynamicDisplay As IDynamicDisplay)
Dim lapCount As String
Dim lapInfo As String
Dim lapHeartRateInfo As String
SyncLock m_bikePositionInfo
lapCount = String.Format("Lap #{0}", m_bikePositionInfo.lapCount)
lapInfo = String.Format("Lap information:" & Constants.vbLf & "Distance: {0}m" & Constants.vbLf & "Maximum speed - {1}" & Constants.vbLf & "Calories - {2}", m_bikePositionInfo.lapDistanceMeters.ToString("#####.#"), m_bikePositionInfo.lapMaximumSpeed.ToString("###.#"), m_bikePositionInfo.lapCalories)
lapHeartRateInfo = String.Format("Lap heart rate info:" & Constants.vbLf & "Average - {0}" & Constants.vbLf & "Maximum - {1}", m_bikePositionInfo.lapAverageHeartRate, m_bikePositionInfo.lapMaximumHeartRate)
End SyncLock
m_dynamicSymbolProperties.DynamicGlyph(esriDynamicSymbolType.esriDSymbolMarker)= m_catGlyph
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f, 1.0f, 1.0f)
m_dynamicSymbolProperties.SetScale(esriDynamicSymbolType.esriDSymbolMarker, 1.0f, 1.0f)
m_dynamicSymbolProperties.RotationAlignment(esriDynamicSymbolType.esriDSymbolMarker)= esriDynamicSymbolRotationAlignment.esriDSRAScreen
m_dynamicSymbolProperties.Heading(esriDynamicSymbolType.esriDSymbolMarker)= 0.0f
m_dynamicSymbolProperties.TextBoxUseDynamicFillSymbol = True
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolText, 0.0f, 0.8f, 0.0f, 1.0f)
m_dynamicSymbolProperties.SetColor(esriDynamicSymbolType.esriDSymbolFill, 0.0f, 0.0f, 0.0f, 1.0f)
m_dynamicSymbolProperties.TextBoxHorizontalAlignment = esriTextHorizontalAlignment.esriTHALeft
m_textGlyph.SetAnchor(0.0f, 0.0f)
m_dynamicSymbolProperties.DynamicGlyph(esriDynamicSymbolType.esriDSymbolText)= m_textGlyph
Dim strLapInfo As String() = New String() { lapCount, lapInfo, lapHeartRateInfo }
m_dynamicCompoundMarker.DrawScreenArrayMarker(m_additionalInfoPoint, nullString, nullString, strLapInfo, nullString, nullString)
End Sub
Private Function GetPlaybackXmlPath() As String
Dim ofd As OpenFileDialog = New OpenFileDialog()
ofd.CheckFileExists = True
ofd.Multiselect = False
ofd.Filter = "HST files (*.hst)|*.hst|GPX files (*.gpx)|*.gpx|XML files (*.xml)|*.xml"
ofd.RestoreDirectory = True
ofd.Title = "Input biking log file"
Dim result As DialogResult = ofd.ShowDialog()
If result = System.Windows.Forms.DialogResult.OK Then
Dim fileExtension As String = System.IO.Path.GetExtension(ofd.FileName).ToUpper()
If fileExtension = ".GPX" Then
m_playbackFormat = GPSPlaybackFormat.GPX
ElseIf fileExtension = ".HST" Then
m_playbackFormat = GPSPlaybackFormat.HST
ElseIf fileExtension = ".XML" Then
m_playbackFormat = GPSPlaybackFormat.XML
End If
Return ofd.FileName
End If
Return String.Empty
End Function
#End Region
End Class