Mini Kabibi Habibi
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Globalization
Imports System.Windows
Imports System.Windows.Data
Imports System.Windows.Media
Imports System.Windows.Threading
Imports System.Xml.Linq
Imports DevExpress.Xpf.Map
Namespace MapDemo
Partial Public Class MapElements
Inherits MapDemoModule
Private dataGenerator As FlightMapDataGenerator
Public Sub New()
InitializeComponent()
dataGenerator = New FlightMapDataGenerator(TryCast(Resources("airportTemplate"), DataTemplate), TryCast(Resources("planeTemplate"), DataTemplate), planeInfoPanel)
DataContext = dataGenerator
dataGenerator.SpeedScale = Convert.ToDouble(tbSpeedScale.Value)
End Sub
Private Sub tbSpeedScale_EditValueChanged(ByVal sender As Object, ByVal e As DevExpress.Xpf.Editors.EditValueChangedEventArgs)
If dataGenerator IsNot Nothing Then
dataGenerator.SpeedScale = Convert.ToDouble(e.NewValue)
End If
End Sub
End Class
Public Class PlaneTrajectory
Private Class TrajectoryPart
Private ReadOnly startPointField As GeoPoint
Private ReadOnly endPointField As GeoPoint
Private ReadOnly flightTimeField As Double
Private ReadOnly courseField As Double
Public ReadOnly Property StartPoint() As GeoPoint
Get
Return startPointField
End Get
End Property
Public ReadOnly Property EndPoint() As GeoPoint
Get
Return endPointField
End Get
End Property
Public ReadOnly Property FlightTime() As Double
Get
Return flightTimeField
End Get
End Property
Public ReadOnly Property Course() As Double
Get
Return courseField
End Get
End Property
Public Sub New(ByVal projection As IProjection, ByVal startPoint As GeoPoint, ByVal endPoint As GeoPoint, ByVal speedInKmH As Double)
Me.startPointField = startPoint
Me.endPointField = endPoint
Dim sizeInKm As Size = projection.GeoToKilometersSize(startPoint, New Size(Math.Abs(startPoint.Longitude - endPoint.Longitude), Math.Abs(startPoint.Latitude - endPoint.Latitude)))
Dim partlength As Double = Math.Sqrt(sizeInKm.Width * sizeInKm.Width + sizeInKm.Height * sizeInKm.Height)
flightTimeField = partlength / speedInKmH
courseField = Math.Atan2((endPoint.Longitude - startPoint.Longitude), (endPoint.Latitude - startPoint.Latitude)) * 180 / Math.PI
End Sub
Public Function GetPointByCurrentFlightTime(ByVal currentFlightTime As Double, ByVal projection As IProjection) As GeoPoint
If currentFlightTime > FlightTime Then
Return endPointField
End If
Dim ratio As Double = currentFlightTime / FlightTime
Return New GeoPoint(startPointField.Latitude + ratio * (endPointField.Latitude - startPointField.Latitude), startPointField.Longitude + ratio * (endPointField.Longitude - startPointField.Longitude))
End Function
End Class
Private ReadOnly trajectory As New List(Of TrajectoryPart)()
Public ReadOnly Property StartPoint() As GeoPoint
Get
Return If((trajectory.Count > 0), trajectory(0).StartPoint, New GeoPoint(0, 0))
End Get
End Property
Public ReadOnly Property EndPoint() As GeoPoint
Get
Return If((trajectory.Count > 0), trajectory(trajectory.Count - 1).EndPoint, New GeoPoint(0, 0))
End Get
End Property
Public ReadOnly Property FlightTime() As Double
Get
Dim result As Double = 0.0
For Each part As TrajectoryPart In trajectory
result += part.FlightTime
Next part
Return result
End Get
End Property
Public Sub New(ByVal points As List(Of GeoPoint), ByVal speedInKmH As Double)
Dim projection As New SphericalMercatorProjection()
For i As Integer = 0 To points.Count - 2
trajectory.Add(New TrajectoryPart(projection, points(i), points(i + 1), speedInKmH))
Next i
End Sub
Public Function GetPointByCurrentFlightTime(ByVal currentFlightTime As Double) As GeoPoint
Dim projection As New SphericalMercatorProjection()
Dim time As Double = 0.0
For i As Integer = 0 To trajectory.Count - 2
If trajectory(i).FlightTime > currentFlightTime - time Then
Return trajectory(i).GetPointByCurrentFlightTime(currentFlightTime - time, projection)
End If
time += trajectory(i).FlightTime
Next i
Return trajectory(trajectory.Count - 1).GetPointByCurrentFlightTime(currentFlightTime - time, projection)
End Function
Public Function GetAirPath() As GeoPointCollection
Dim result As New GeoPointCollection()
Dim currentFlightTime As Double = 0.0
Do While currentFlightTime < FlightTime
result.Add(GetPointByCurrentFlightTime(currentFlightTime))
currentFlightTime += 0.001
Loop
result.Add(GetPointByCurrentFlightTime(FlightTime))
Return result
End Function
Public Function GetCourseByCurrentFlightTime(ByVal currentFlightTime As Double) As Double
Dim time As Double = 0.0
For i As Integer = 0 To trajectory.Count - 2
If trajectory(i).FlightTime > currentFlightTime - time Then
Return trajectory(i).Course
End If
time += trajectory(i).FlightTime
Next i
Return trajectory(trajectory.Count - 1).Course
End Function
End Class
Public Class PlaneInfo
Inherits DependencyObject
Public Shared ReadOnly CurrentFlightTimeProperty As DependencyProperty = DependencyProperty.Register("CurrentFlightTime", GetType(Double), GetType(PlaneInfo), New PropertyMetadata(0.0, New PropertyChangedCallback(AddressOf CurrentFlightTimePropertyChanged)))
Public Shared ReadOnly PositionProperty As DependencyProperty = DependencyProperty.Register("Position", GetType(GeoPoint), GetType(PlaneInfo), New PropertyMetadata(New GeoPoint(0, 0)))
Public Shared ReadOnly CourseProperty As DependencyProperty = DependencyProperty.Register("Course", GetType(Double), GetType(PlaneInfo), New PropertyMetadata(0.0))
Public Property CurrentFlightTime() As Double
Get
Return CDbl(GetValue(CurrentFlightTimeProperty))
End Get
Set(ByVal value As Double)
SetValue(CurrentFlightTimeProperty, value)
End Set
End Property
Public ReadOnly Property Position() As GeoPoint
Get
Return CType(GetValue(PositionProperty), GeoPoint)
End Get
End Property
Public ReadOnly Property Course() As Double
Get
Return CDbl(GetValue(CourseProperty))
End Get
End Property
Private Shared Sub CurrentFlightTimePropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
Dim info As PlaneInfo = TryCast(d, PlaneInfo)
If info IsNot Nothing Then
info.UpdatePosition(CDbl(e.NewValue))
End If
End Sub
Private Shared Function ConvertPlaneNameToFilePath(ByVal PlaneName As String) As String
Dim result As String = PlaneName.Replace(" ", "")
result = "../Images/Planes/" & result.Replace("-", "") & ".png"
Return result
End Function
Private isLandedField As Boolean = False
Private ReadOnly planeIDField As String
Private ReadOnly nameField As String
Private ReadOnly endPointNameField As String
Private ReadOnly startPointNameField As String
Private ReadOnly speedInKmHField As Double
Private ReadOnly flightAltitudeField As Double
Private ReadOnly imagePathField As String
Private ReadOnly trajectoryField As PlaneTrajectory
Public ReadOnly Property PlaneID() As String
Get
Return planeIDField
End Get
End Property
Public ReadOnly Property Name() As String
Get
Return nameField
End Get
End Property
Public ReadOnly Property EndPointName() As String
Get
Return endPointNameField
End Get
End Property
Public ReadOnly Property StartPointName() As String
Get
Return startPointNameField
End Get
End Property
Public ReadOnly Property SpeedKmH() As Double
Get
Return If(isLandedField, 0.0, speedInKmHField)
End Get
End Property
Public ReadOnly Property FlightAltitude() As Double
Get
Return If(isLandedField, 0.0, flightAltitudeField)
End Get
End Property
Public ReadOnly Property ImagePath() As String
Get
Return imagePathField
End Get
End Property
Public ReadOnly Property IsLanded() As Boolean
Get
Return isLandedField
End Get
End Property
Public ReadOnly Property TotalFlightTime() As Double
Get
Return trajectoryField.FlightTime
End Get
End Property
Public Sub New(ByVal name As String, ByVal id As String, ByVal endPointName As String, ByVal startPointName As String, ByVal speedInKmH As Double, ByVal flightAltitude As Double, ByVal points As List(Of GeoPoint))
Me.nameField = name
Me.planeIDField = id
Me.endPointNameField = endPointName
Me.startPointNameField = startPointName
Me.speedInKmHField = speedInKmH
Me.flightAltitudeField = flightAltitude
imagePathField = ConvertPlaneNameToFilePath(name)
trajectoryField = New PlaneTrajectory(points, speedInKmH)
UpdatePosition(CurrentFlightTime)
End Sub
Private Sub UpdatePosition(ByVal flightTime As Double)
Dim pos As GeoPoint = trajectoryField.GetPointByCurrentFlightTime(flightTime)
Dim course As Double = trajectoryField.GetCourseByCurrentFlightTime(flightTime)
isLandedField = flightTime >= trajectoryField.FlightTime
SetValue(PositionProperty, pos)
SetValue(CourseProperty, course)
End Sub
Public Function GetAirPath(ByVal airportTemplate As DataTemplate) As List(Of MapItem)
Dim mapItemList As New List(Of MapItem)()
mapItemList.Add(New MapPolyline() With {.Points = trajectoryField.GetAirPath(), .Fill = New SolidColorBrush(Colors.Transparent), .Stroke = New SolidColorBrush(Color.FromArgb(127, 255, 0, 199)), .StrokeStyle = New StrokeStyle() With {.Thickness = 4}})
mapItemList.Add(New MapCustomElement() With {.Location = trajectoryField.StartPoint, .ContentTemplate = airportTemplate})
mapItemList.Add(New MapCustomElement() With {.Location = trajectoryField.EndPoint, .ContentTemplate = airportTemplate})
Return mapItemList
End Function
End Class
Public Class FlightMapDataGenerator
Inherits DependencyObject
Public Shared ReadOnly PlanesProperty As DependencyProperty = DependencyProperty.Register("Planes", GetType(ObservableCollection(Of MapCustomElement)), GetType(FlightMapDataGenerator), New PropertyMetadata(Nothing))
Public Shared ReadOnly ActualAirPathProperty As DependencyProperty = DependencyProperty.Register("ActualAirPath", GetType(ObservableCollection(Of MapItem)), GetType(FlightMapDataGenerator), New PropertyMetadata(Nothing))
Public Shared ReadOnly ActualPlaneInfoProperty As DependencyProperty = DependencyProperty.Register("ActualPlaneInfo", GetType(PlaneInfo), GetType(FlightMapDataGenerator), New PropertyMetadata(Nothing))
Public Shared ReadOnly SelectedPlaneProperty As DependencyProperty = DependencyProperty.Register("SelectedPlane", GetType(MapCustomElement), GetType(FlightMapDataGenerator), New PropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf SelectedPlaneProperyChanged)))
Public ReadOnly Property Planes() As ObservableCollection(Of MapCustomElement)
Get
Return CType(GetValue(PlanesProperty), ObservableCollection(Of MapCustomElement))
End Get
End Property
Public ReadOnly Property ActualAirPath() As ObservableCollection(Of MapItem)
Get
Return CType(GetValue(ActualAirPathProperty), ObservableCollection(Of MapItem))
End Get
End Property
Public Property ActualPlaneInfo() As PlaneInfo
Get
Return CType(GetValue(ActualPlaneInfoProperty), PlaneInfo)
End Get
Set(ByVal value As PlaneInfo)
SetValue(ActualPlaneInfoProperty, value)
End Set
End Property
Public Property SelectedPlane() As MapCustomElement
Get
Return CType(GetValue(SelectedPlaneProperty), MapCustomElement)
End Get
Set(ByVal value As MapCustomElement)
SetValue(SelectedPlaneProperty, value)
End Set
End Property
Public Property SpeedScale() As Double
Get
Return speedScaleField
End Get
Set(ByVal value As Double)
speedScaleField = value
End Set
End Property
Private Shared Sub SelectedPlaneProperyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
Dim flightMapDataGenerator As FlightMapDataGenerator = TryCast(d, FlightMapDataGenerator)
If flightMapDataGenerator IsNot Nothing Then
flightMapDataGenerator.UpdatePlaneInfo(TryCast(e.NewValue, MapCustomElement))
End If
End Sub
Private Const mSecPerHour As Double = 3600000
Private ReadOnly timer As New DispatcherTimer()
Private ReadOnly airportTemplate As DataTemplate
Private ReadOnly planesInfo As New List(Of PlaneInfo)()
Private ReadOnly infoPanel As PlaneInfoPanel
Private speedScaleField As Double
Private lastTime As DateTime
Public Sub New(ByVal airportTemplate As DataTemplate, ByVal planeTemplate As DataTemplate, ByVal infoPanel As PlaneInfoPanel)
Me.SetValue(PlanesProperty, New ObservableCollection(Of MapCustomElement)())
Me.SetValue(ActualAirPathProperty, New ObservableCollection(Of MapItem)())
Me.airportTemplate = airportTemplate
Me.infoPanel = infoPanel
LoadFromXML(planeTemplate)
AddHandler timer.Tick, AddressOf OnTimedEvent
timer.Interval = New TimeSpan(0, 0, 2)
lastTime = DateTime.Now
timer.Start()
If Planes IsNot Nothing Then
SelectedPlane = Planes(1)
End If
End Sub
Private Sub LoadFromXML(ByVal planeTemplate As DataTemplate)
Dim document As XDocument = DataLoader.LoadXmlFromResources("/Data/FlightMap.xml")
If document IsNot Nothing Then
For Each element As XElement In document.Element("Planes").Elements()
Dim points As New List(Of GeoPoint)()
For Each infoElement As XElement In element.Element("Path").Elements()
Dim geoPoint As New GeoPoint(Convert.ToDouble(infoElement.Element("Latitude").Value, CultureInfo.InvariantCulture), Convert.ToDouble(infoElement.Element("Longitude").Value, CultureInfo.InvariantCulture))
points.Add(geoPoint)
Next infoElement
Dim info As New PlaneInfo(element.Element("PlaneName").Value, element.Element("PlaneID").Value, element.Element("EndPointName").Value, element.Element("StartPointName").Value, Convert.ToInt32(element.Element("Speed").Value), Convert.ToInt32(element.Element("Altitude").Value), points)
info.CurrentFlightTime = Convert.ToDouble(element.Element("CurrentFlightTime").Value, CultureInfo.InvariantCulture)
planesInfo.Add(info)
Next element
End If
For Each info As PlaneInfo In planesInfo
Dim mapCustomElement As New MapCustomElement() With {.Content = info, .ContentTemplate = planeTemplate}
BindingOperations.SetBinding(mapCustomElement, MapCustomElement.LocationProperty, New Binding("Position") With {.Source = info})
Planes.Add(mapCustomElement)
Next info
End Sub
Private Sub UpdateCurrentPath(ByVal planeInfo As PlaneInfo)
ActualAirPath.Clear()
If planeInfo IsNot Nothing Then
For Each item As MapItem In planeInfo.GetAirPath(airportTemplate)
ActualAirPath.Add(item)
Next item
End If
End Sub
Private Sub UpdatePlaneInfo(ByVal plane As MapCustomElement)
ActualPlaneInfo = If((plane IsNot Nothing), (TryCast(plane.Content, PlaneInfo)), Nothing)
infoPanel.Visible = ActualPlaneInfo IsNot Nothing
UpdateCurrentPath(ActualPlaneInfo)
End Sub
Private Sub OnTimedEvent(ByVal source As Object, ByVal e As EventArgs)
Dim currentTime As DateTime = DateTime.Now
Dim interval As TimeSpan = currentTime.Subtract(lastTime)
For Each info As PlaneInfo In planesInfo
If (Not info.IsLanded) Then
info.CurrentFlightTime += speedScaleField * interval.TotalMilliseconds / mSecPerHour
End If
Next info
lastTime = currentTime
End Sub
End Class
End Namespace