Extension.vb
' Copyright 2011 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. ' Imports System Imports System.Collections.Generic Imports System.ComponentModel Imports System.Data Imports System.Linq Imports System.Text Imports System.Drawing Imports ESRI.ArcGISExplorer Imports ESRI.ArcGISExplorer.Application Imports ESRI.ArcGISExplorer.Geometry Imports ESRI.ArcGISExplorer.Mapping Imports ESRI.ArcGISExplorer.Data Imports ESRI.ArcGISExplorer.Threading Public Class Extension Inherits ESRI.ArcGISExplorer.Application.Extension Private _bgWorker As ESRI.ArcGISExplorer.Threading.BackgroundWorker Private _timer As System.Windows.Forms.Timer Private Shared _vehicles As List(Of Vehicle) Private _ambulanceSymbol As Symbol Private _fileCount As Integer Private _dataPath As String = String.Empty Private _dataOk As Boolean = False ''' <summary> ''' Called by Explorer when the application starts up. Work out the path to the Data, ''' and hook up the document open event. ''' </summary> Public Overrides Sub OnStartup() Dim sdkPath As String = Environment.GetEnvironmentVariable("ArcGIS_E3SDK") _dataPath = sdkPath + "\Samples\VehicleTrackingExt\Data\" If (System.IO.Directory.Exists(_dataPath)) Then _dataOk = True End If If (_dataOk) Then AddHandler Application.DocumentOpened, AddressOf Application_DocumentOpened AddHandler Application.DocumentClosed, AddressOf Application_DocumentClosed End If 'Setup the background worker InitializeBackgroundWorker() End Sub ''' <summary> ''' Called by Explorer when the application shuts down. Stop the Timer, unhook any event handlers. ''' </summary> Public Overrides Sub OnShutdown() If Not _timer Is Nothing Then _timer.Stop() _timer = Nothing End If RemoveHandler Application.DocumentOpened, AddressOf Application_DocumentOpened RemoveHandler Application.DocumentClosed, AddressOf Application_DocumentClosed End Sub ''' <summary> ''' Initializes the vehicle list and the background worker, listen for graphic clicked events ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub Application_DocumentOpened(ByVal sender As Object, ByVal e As EventArgs) InitializeBackgroundWorker() End Sub Private Sub InitializeBackgroundWorker() AddHandler Application.ActiveMapDisplay.GraphicClicked, AddressOf ActiveMapDisplay_GraphicClicked _vehicles = New List(Of Vehicle)() _bgWorker = New ESRI.ArcGISExplorer.Threading.BackgroundWorker() AddHandler _bgWorker.DoWork, AddressOf DoWork AddHandler _bgWorker.RunWorkerCompleted, AddressOf RunWorkerCompleted If (Not _bgWorker.IsBusy) Then ' Best practice to always check IsBusy before calling RunWorkerAsync; although in this specific ' case as the object has just been created its unlikely to be busy. _bgWorker.RunWorkerAsync(_dataPath) End If End Sub ''' <summary> ''' Stop the timer and remove the graphics from the map ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub Application_DocumentClosed(ByVal sender As Object, ByVal e As EventArgs) If Not _timer Is Nothing Then _timer.Stop() End If _timer = Nothing ' Unhook event handlers while there is no document. RemoveHandler Application.ActiveMapDisplay.GraphicClicked, AddressOf ActiveMapDisplay_GraphicClicked If (Not _bgWorker Is Nothing) Then RemoveHandler _bgWorker.DoWork, AddressOf DoWork RemoveHandler _bgWorker.RunWorkerCompleted, AddressOf RunWorkerCompleted _bgWorker = Nothing End If End Sub ''' <summary> ''' Re-execute the background worker to poll the GeoRSS feed. ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub _timer_Tick(ByVal sender As Object, ByVal e As EventArgs) If ((Not _bgWorker Is Nothing) AndAlso (Not _bgWorker.IsBusy)) Then _bgWorker.RunWorkerAsync(_dataPath) End If End Sub ''' <summary> ''' Called on the worker thread. Queries a GeoRSS feed to get a list of emergency vehicles ''' and their locations and updates a list of known vehicles from previous queries ''' on the feed. ''' NOTE: In order to provide reliable sample code, this reads files saved from a GeoRSS feed, ''' not a live GeoRSS feed. ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub DoWork(ByVal sender As Object, ByVal e As ESRI.ArcGISExplorer.Threading.DoWorkEventArgs) ' Pull vehicle records from the GeoRSS feed / files. Dim ds As DataSet = New DataSet() Dim path As String = CType(e.Argument, String) _fileCount += 1 If _fileCount > 20 Then _fileCount = 1 End If Dim filePath As String = path + "GetAmbulances" + _fileCount.ToString() + ".xml" _dataOk = System.IO.File.Exists(filePath) If _dataOk Then ds.ReadXml(filePath, XmlReadMode.Auto) Dim table As DataTable = ds.Tables(0) ProcessVehicles(table) End If End Sub ''' <summary> ''' Called on the UI thread. Update the vehicle locations on the map, add and delete ''' any new or removed vehicles. ''' The first time this method is called for the document, initializes a timer to update ''' the vehicles at a regular interval. ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub RunWorkerCompleted(ByVal sender As Object, ByVal e As ESRI.ArcGISExplorer.Threading.RunWorkerCompletedEventArgs) 'Don't update the graphic display if the user is in the process of navigating If ESRI.ArcGISExplorer.Application.Application.ActiveMapDisplay.IsNavigating Then Return If (Not e.Error Is Nothing) Then ' There was an exception thrown within the DoWork event. ' May wish to investigate here. Debug.WriteLine(e.Error.ToString()) End If If (Not _vehicles Is Nothing) Then 'Put the updating of vehicles inside a SuspendDrawing block for better performance Dim mapDisp As MapDisplay = ESRI.ArcGISExplorer.Application.Application.ActiveMapDisplay Using mapDisp.SuspendDisplay Dim graphics As GraphicCollection = Application.ActiveMapDisplay.Graphics For Each veh As Vehicle In _vehicles If veh.GraphicUpdateStatus = UpdateStatus.Delete Then graphics.Remove(veh.MapGraphic) End If Next veh GetVehicleGraphics() For Each veh As Vehicle In _vehicles If (Not graphics.Contains(veh.MapGraphic)) Then graphics.Add(veh.MapGraphic) End If Next veh End Using End If ' If this is the first time the worker has completed, then start the timer to start ' generating more updates. If _timer Is Nothing Then _timer = New System.Windows.Forms.Timer() _timer.Interval = (5000) AddHandler _timer.Tick, AddressOf _timer_Tick _timer.Start() End If End Sub ''' <summary> ''' Creates Vehicle objects from the GeoRSS feed records, adds them if they are new and ''' marks them as updated if they have changed ''' </summary> ''' <param name="table"></param> Private Sub ProcessVehicles(ByVal table As DataTable) For Each row As DataRow In table.Rows Dim newVehicle As Vehicle = New Vehicle() newVehicle.X = Convert.ToDouble(row.ItemArray.GetValue(1).ToString()) newVehicle.Y = Convert.ToDouble(row.ItemArray.GetValue(2).ToString()) newVehicle.Status = row.ItemArray.GetValue(5).ToString() newVehicle.ID = row.ItemArray.GetValue(0).ToString() newVehicle.Speed() = row.ItemArray.GetValue(3).ToString() newVehicle.Bearing() = row.ItemArray.GetValue(4).ToString() newVehicle.Name() = row.ItemArray.GetValue(6).ToString() newVehicle.GraphicUpdateStatus() = UpdateStatus.None ' verify the vehicle exists Dim existing As Vehicle = FindVehicle(row.ItemArray.GetValue(0).ToString()) If existing Is Nothing Then _vehicles.Add(newVehicle) ElseIf existing.X <> newVehicle.X OrElse existing.Y <> newVehicle.Y Then existing.X = newVehicle.X existing.Y = newVehicle.Y existing.GraphicUpdateStatus = UpdateStatus.Update Else existing.GraphicUpdateStatus = UpdateStatus.None End If Next row End Sub ''' <summary> ''' Creates graphics for the new vehicles from the feed and updates those that ''' have been updated since the last poll ''' </summary> Private Sub GetVehicleGraphics() If _ambulanceSymbol Is Nothing Then _ambulanceSymbol = Symbol.Marker.Health.Ambulance End If If (Not _vehicles Is Nothing) Then For Each vehicle As Vehicle In _vehicles Dim xoPoint As ESRI.ArcGISExplorer.Geometry.Point = New ESRI.ArcGISExplorer.Geometry.Point(vehicle.X, vehicle.Y) If vehicle.MapGraphic Is Nothing Then Dim vehicleGraphic As Graphic = New Graphic(xoPoint, _ambulanceSymbol, vehicle.ID) vehicleGraphic.Placement3D = Placement3D.AttachToSurface vehicle.MapGraphic = vehicleGraphic ElseIf vehicle.GraphicUpdateStatus = UpdateStatus.Update Then vehicle.MapGraphic.MoveTo(xoPoint.X, xoPoint.Y) vehicle.GraphicUpdateStatus = UpdateStatus.None End If Next vehicle End If End Sub ''' <summary> ''' Finds a vehicle in the collection based on its ID or graphic ''' </summary> ''' <param name="ID"></param> ''' <returns></returns> Private Shared Function FindVehicle(ByVal ID As String) As Vehicle If (Not _vehicles Is Nothing) Then For Each vehicle As Vehicle In _vehicles If vehicle.ID = ID Then Return vehicle End If Next vehicle End If Return Nothing End Function ''' <summary> ''' Finds a vehicle with the specified graphic ''' </summary> ''' <param name="g"></param> ''' <returns></returns> Private Shared Function FindVehicle(ByVal g As Graphic) As Vehicle If (Not _vehicles Is Nothing) Then For Each veh As Vehicle In _vehicles If veh.MapGraphic Is g Then Return veh End If Next veh End If Return Nothing End Function ''' <summary> ''' Event handler when a graphic is clicked on the map: open its infopopup window ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub ActiveMapDisplay_GraphicClicked(ByVal sender As Object, ByVal e As GraphicMouseEventArgs) Dim vehicle As Vehicle = FindVehicle(e.Graphics(0)) ' Use first clicked. If Not vehicle Is Nothing Then ShowPopup(vehicle) End If End Sub ''' <summary> ''' Display the popup for the specified vehicle ''' </summary> ''' <param name="vehicle"></param> Public Shared Sub ShowPopup(ByVal vehicle As Vehicle) Dim g As Graphic = vehicle.MapGraphic Dim content As String = "<b>" & vehicle.ID & "</b>" & "<br><br>Driver: " & vehicle.Name & "<br>" & "Speed: " & vehicle.Speed & "<br>" & "Bearing: " & vehicle.Bearing & "<br>" & "Status: " & vehicle.Status Application.ActiveMapDisplay.HidePopups(True) Dim myPopup As Popup = New Popup(Application.ActiveMapDisplay, content, "Vehicle Information", DirectCast(g.Geometry, ESRI.ArcGISExplorer.Geometry.Point)) myPopup.Activate() End Sub End Class