Selaa lähdekoodia

Trying to improve Live Maps

frogsoftware 1 vuosi sitten
vanhempi
commit
bae17634bd

+ 42 - 0
prs.desktop/Panels/Maps/MapMarker.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+namespace PRSDesktop;
+
+public class MapMarker : DependencyObject
+{
+    public Guid ID { get; set; }
+    
+    private static readonly DependencyProperty LatitudeProperty =
+        DependencyProperty.Register(nameof(Latitude), typeof(string), typeof(MapMarker));
+    public string? Latitude
+    {
+        get => GetValue(LatitudeProperty) as string;
+        set => SetValue(LatitudeProperty, value);
+    }
+    
+    private static readonly DependencyProperty LongitudeProperty =
+        DependencyProperty.Register(nameof(Longitude), typeof(string), typeof(MapMarker));
+    
+    public string? Longitude
+    {
+        get => GetValue(LongitudeProperty) as string;
+        set => SetValue(LongitudeProperty, value);
+    }
+
+    private static readonly DependencyProperty LabelProperty =
+        DependencyProperty.Register(nameof(Label), typeof(string), typeof(MapMarker));
+    
+    public string? Label
+    {
+        get => GetValue(LabelProperty) as string;
+        set => SetValue(LabelProperty, value);
+    }
+    
+    public string Description { get; set; }
+    public DateTime Updated { get; set; }
+    public string UpdatedBy { get; set; }
+    public string DeviceID { get; set; }
+    public Brush Background { get; internal set; }
+}

+ 105 - 0
prs.desktop/Panels/Maps/MapViewModel.cs

@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Windows;
+using System.Windows.Media;
+using InABox.WPF;
+using net.sf.mpxj;
+using Syncfusion.UI.Xaml.Maps;
+
+namespace PRSDesktop;
+
+public enum MapViewType
+{
+    Current,
+    History
+}
+
+public class MapViewModel : DependencyObject
+{
+    
+    private readonly List<MapMarker> _markers = new();
+    private readonly List<MapMarker> _waypoints = new();
+    private MapMarker? _selected;
+
+    public MapViewModel()
+    {
+        Markers = new ObservableCollection<MapMarker>();
+        WayPoints = new ObservableCollection<Point>();
+    }
+
+    public ObservableCollection<MapMarker> Markers { get; }
+    public ObservableCollection<Point> WayPoints { get; }
+    
+    static readonly DependencyProperty ViewProperty =
+        DependencyProperty.Register(
+            nameof(View),
+            typeof(MapViewType),
+            typeof(MapViewModel));
+
+    public MapViewType View
+    {
+        get => (MapViewType)GetValue(ViewProperty);
+        set
+        {
+            SetValue(ViewProperty, value);
+            Refresh();
+        }
+    }
+
+    public MapMarker Selected
+    {
+        get => _selected;
+        set
+        {
+            _selected = value;
+            Refresh();
+        }
+    }
+
+    public void AddWayPoint(MapMarker waypoint)
+    {
+        _waypoints.Add(waypoint);
+    }
+
+    public void AddMarker(MapMarker marker, bool refresh = false)
+    {
+        _markers.Add(marker);
+        if (refresh)
+            Refresh();
+    }
+
+    public void Clear()
+    {
+        _selected = null;
+        _markers.Clear();
+        _waypoints.Clear();
+        Refresh();
+    }
+
+    public void Refresh()
+    {
+        Markers.Clear();
+        WayPoints.Clear();
+        if (View == MapViewType.Current)
+        {
+            foreach (var marker in _markers)
+            {
+                marker.Background = new SolidColorBrush(marker == _selected ? Colors.Yellow : Colors.Orange);
+                if (marker != _selected)
+                    Markers.Add(marker);
+            }
+            if (_selected != null)
+                Markers.Add(_selected);
+        }
+        else if (_selected != null)
+        {
+            foreach (var waypoint in _waypoints)
+            {
+                Markers.Add(waypoint);
+                WayPoints.Add(new Point(double.Parse(waypoint.Longitude), double.Parse(waypoint.Latitude)));
+            }
+        }
+
+
+    }
+}

+ 69 - 24
prs.desktop/Panels/Maps/MapsPanel.xaml

@@ -5,8 +5,54 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
              xmlns:dynamicGrid="clr-namespace:InABox.DynamicGrid;assembly=InABox.Wpf"
+             xmlns:prsDesktop="clr-namespace:PRSDesktop"
              mc:Ignorable="d"
              d:DesignHeight="450" d:DesignWidth="800">
+    
+    <UserControl.DataContext>
+        <prsDesktop:MapViewModel x:Name="ViewModel" />
+    </UserControl.DataContext>
+    
+    <UserControl.Resources>
+        <prsDesktop:TestConverter x:Key="TestConverter" />
+        <DataTemplate x:Key="MapMarkerTemplate" DataType="{x:Type syncfusion:CustomDataSymbol}"  >
+            <Border
+                    x:Name="_border"
+                    CornerRadius="0,10,10,10"
+                    BorderBrush="Black"
+                    BorderThickness="0.75"
+                    Background="{Binding ., Converter={StaticResource TestConverter}}"
+                    Padding="5,2,5,2"
+                    PreviewMouseDown="Image_PreviewMouseDown">
+                <Label
+                    Content="{Binding ., Converter={StaticResource TestConverter}}"
+                    HorizontalContentAlignment="Center"
+                    VerticalContentAlignment="Center"
+                    FontSize="10"
+                    Background="Red"
+                    Foreground="Yellow"/>
+            </Border>
+        </DataTemplate>
+        <!-- -->
+        <!-- <DataTemplate x:Key="MapWayPointTemplate" DataType="{x:Type prsDesktop:MapMarker}"> -->
+        <!--     <Border  -->
+        <!--         x:Name="_border"  -->
+        <!--         CornerRadius="0,10,10,10" -->
+        <!--         BorderBrush="Black"  -->
+        <!--         BorderThickness="0.75"  -->
+        <!--         Background="Green" -->
+        <!--         Padding="5,2,5,2" > -->
+        <!--         <Label  -->
+        <!--             x:Name="Code"  -->
+        <!--             Content="Time"  -->
+        <!--             HorizontalContentAlignment="Center" -->
+        <!--             VerticalContentAlignment="Center" -->
+        <!--             FontSize="10" -->
+        <!--             Background="Yellow" -->
+        <!--             Foreground="Red"/> -->
+        <!--     </Border> -->
+        <!-- </DataTemplate> -->
+    </UserControl.Resources>
     <Grid>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="Auto" />
@@ -23,41 +69,32 @@
             <Slider x:Name="Zoom" Orientation="Vertical" ValueChanged="Zoom_ValueChanged" Minimum="1" Maximum="20"
                     Value="11" />
         </Border>
+        
         <syncfusion:SfMap x:Name="Map" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" BorderBrush="Gray"
                           BorderThickness="0.75" PreviewMouseWheel="Map_PreviewMouseWheel"
                           PreviewMouseDoubleClick="Map_PreviewMouseDoubleClick" PreviewMouseUp="Map_PreviewMouseUp">
             <syncfusion:SfMap.Layers>
                 <syncfusion:ImageryLayer
+                    x:Name="ImageryLayer"
                     LayerType="Bing"
                     BingMapKey="5kn7uPPBBGKdmWYiwHJL~LjUY7IIgFzGdXpkpWT7Fsw~AmlksNuYOBHNQ3cOa61Nz2ghvK5EbrBZ3aQqvTS4OjcPxTBGNGsj2hKc058CgtgJ"
                     BingMapStyle="Road"
                     Center="-31.95105, 115.85939"
                     Radius="10"
-                    Markers="{Binding Markers}">
-                    <syncfusion:ImageryLayer.MarkerTemplate>
-                        <DataTemplate>
-                            <Border x:Name="_border" DataContext="{Binding Data}" CornerRadius="0,10,10,10"
-                                    BorderBrush="Black" BorderThickness="0.75" Background="{Binding Background}"
-                                    Padding="5,2,5,2" PreviewMouseDown="Image_PreviewMouseDown">
-                                <TextBlock x:Name="Code" Text="{Binding Code}" TextAlignment="Center" FontSize="10" />
-                            </Border>
-                        </DataTemplate>
-
-                    </syncfusion:ImageryLayer.MarkerTemplate>
+                    Markers="{Binding Markers}"
+                    MarkerTemplate="{StaticResource MapMarkerTemplate}">
+                    <!-- <syncfusion:ImageryLayer.SubShapeFileLayers> -->
+                    <!--     <syncfusion:SubShapeFileLayer > -->
+                    <!--         <syncfusion:SubShapeFileLayer.MapElements > -->
+                    <!--             <syncfusion:MapPolyline Stroke="DarkBlue" StrokeThickness="5" Points="{Binding WayPoints}" /> -->
+                    <!--         </syncfusion:SubShapeFileLayer.MapElements> -->
+                    <!--     </syncfusion:SubShapeFileLayer> -->
+                    <!-- </syncfusion:ImageryLayer.SubShapeFileLayers> -->
+                    
                 </syncfusion:ImageryLayer>
             </syncfusion:SfMap.Layers>
         </syncfusion:SfMap>
-
-        <!--<DockPanel Grid.Column="0" Grid.Row="3">
-            <Button x:Name="ZoomIn" Height="40" Width="100" DockPanel.Dock="Left" Content="Zoom In" Margin="0,4,4,0" Click="ZoomIn_Click"/>
-            <Button x:Name="ZoomOut" Height="40" Width="100" DockPanel.Dock="Left" Content="Zoom Out" Margin="0,4,4,0" Click="ZoomOut_Click"/>
-            
-            --><!--<Button x:Name="PanMode" Height="40" Width="100" DockPanel.Dock="Right" Content="Pan Mode" Margin="4,4,0,0" Click="PanMode_Click"/>--><!--
-
-            <Label Content="" DockPanel.Dock="Right"/>
-
-        </DockPanel>-->
-
+        
         <Border Grid.Column="2" Grid.Row="0" Margin="4,0,0,0" BorderBrush="Gray" BorderThickness="0.75"
                 Background="WhiteSmoke">
             <StackPanel Orientation="Vertical">
@@ -75,8 +112,16 @@
             </StackPanel>
         </Border>
 
-        <dynamicGrid:DynamicTabControl Grid.Column="2" Grid.Row="2" Grid.RowSpan="2" Margin="4,2,0,0" BorderBrush="Gray"
-                                       BorderThickness="0.75" Background="WhiteSmoke" TabStripPlacement="Bottom">
+        <dynamicGrid:DynamicTabControl 
+            x:Name="TabView"
+            Grid.Column="2" 
+            Grid.Row="1" 
+            Margin="4,2,0,0" 
+            BorderBrush="Gray"
+            BorderThickness="0.75" 
+            Background="WhiteSmoke" 
+            TabStripPlacement="Bottom" 
+            SelectionChanged="TabView_PageChanged">
             
             <dynamicGrid:DynamicTabItem Header="Marker List">
                 <Grid>

+ 75 - 83
prs.desktop/Panels/Maps/MapsPanel.xaml.cs

@@ -6,7 +6,6 @@ using System.Linq.Expressions;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Input;
-using System.Windows.Media;
 using Comal.Classes;
 using Comal.Stores;
 using InABox.Clients;
@@ -15,74 +14,11 @@ using InABox.Core;
 using InABox.Wpf;
 using Syncfusion.UI.Xaml.Maps;
 using System.ComponentModel;
+using System.Drawing;
+using Point = System.Windows.Point;
 
 namespace PRSDesktop
 {
-    public class MapMarker
-    {
-        public Guid ID { get; set; }
-        public string Latitude { get; set; }
-        public string Longitude { get; set; }
-        public string Code { get; set; }
-        public string Description { get; set; }
-        public DateTime Updated { get; set; }
-        public string UpdatedBy { get; set; }
-        public string DeviceID { get; set; }
-        public Brush Background { get; internal set; }
-    }
-
-    public class ViewModel
-    {
-        private readonly List<MapMarker> _markers = new();
-
-        private MapMarker _selected;
-
-        public ViewModel()
-        {
-            Markers = new ObservableCollection<MapMarker>();
-        }
-
-        public ObservableCollection<MapMarker> Markers { get; }
-
-        public MapMarker Selected
-        {
-            get => _selected;
-            set
-            {
-                _selected = value;
-                Refresh();
-            }
-        }
-
-        public void Add(MapMarker marker, bool refresh = false)
-        {
-            _markers.Add(marker);
-            if (refresh)
-                Refresh();
-        }
-
-        public void Clear()
-        {
-            _selected = null;
-            _markers.Clear();
-            Refresh();
-        }
-
-        public void Refresh()
-        {
-            Markers.Clear();
-            foreach (var marker in _markers)
-            {
-                marker.Background = new SolidColorBrush(marker == _selected ? Colors.Yellow : Colors.Orange);
-                if (marker != _selected)
-                    Markers.Add(marker);
-            }
-
-            if (_selected != null)
-                Markers.Add(_selected);
-        }
-    }
-
     public class ImageryLayerExt : ImageryLayer
     {
         protected override string GetUri(int X, int Y, int Scale)
@@ -108,14 +44,12 @@ namespace PRSDesktop
         private LiveMapsSettings _settings;
 
         private bool bFirst = true;
-        private readonly ViewModel viewmodel = new();
 
         private readonly int ZOOMEDIN = 16;
 
         public MapsPanel()
         {
             InitializeComponent();
-            DataContext = viewmodel;
             //ausgov.Uri = "c:\\development\\maps\\MB_2011_WA.shp";
         }
 
@@ -355,7 +289,7 @@ namespace PRSDesktop
 
         private void LoadDayMarkers()
         {
-            viewmodel.Markers.Clear();
+            ViewModel.Markers.Clear();
         }
 
         private void LoadMarkers<T>(Filter<T> filter, Expression<Func<T, object>> guid, Expression<Func<T, object>> code,
@@ -363,7 +297,7 @@ namespace PRSDesktop
             Expression<Func<T, object>> longitude, Expression<Func<T, object>> updatedby, Expression<Func<T, object>> timestamp, bool first = true)
             where T : Entity, IRemotable, IPersistent, new()
         {
-            viewmodel.Clear();
+            ViewModel.Clear();
             var columns = new Columns<T>(guid, code, description, latitude, longitude, timestamp, updatedby);
             if (deviceid != null && !columns.ColumnNames().Contains(CoreUtils.GetFullPropertyName(deviceid, ".")))
                 columns.Add(deviceid);
@@ -376,7 +310,7 @@ namespace PRSDesktop
                 {
                     Dispatcher.Invoke(() =>
                     {
-                        viewmodel.Markers.Clear();
+                        ViewModel.Markers.Clear();
                         if (error != null)
                         {
                             MessageBox.Show(error.Message);
@@ -401,11 +335,11 @@ namespace PRSDesktop
                             }
 
                             foreach (var key in values.Keys)
-                                viewmodel.Add(
+                                ViewModel.AddMarker(
                                     new MapMarker
                                     {
                                         ID = key,
-                                        Code = values[key].Code,
+                                        Label = values[key].Code,
                                         Latitude = string.Format("{0:F6}", values[key].Latitude),
                                         Longitude = string.Format("{0:F6}", values[key].Longitude),
                                         Description = values[key].Description,
@@ -414,7 +348,7 @@ namespace PRSDesktop
                                         DeviceID = values[key].DeviceID
                                     }
                                 );
-                            viewmodel.Refresh();
+                            ViewModel.Refresh();
                         }
 
                         //if (bFirst)
@@ -434,7 +368,7 @@ namespace PRSDesktop
             var seLon = double.MinValue;
             var seLat = double.MaxValue;
 
-            foreach (var marker in viewmodel.Markers)
+            foreach (var marker in ViewModel.Markers)
             {
                 var lat = double.Parse(marker.Latitude);
                 var lon = double.Parse(marker.Longitude);
@@ -570,12 +504,17 @@ namespace PRSDesktop
                 return;
             LoadDeviceHistory(marker.DeviceID);
         }
+        
+        private class WayPoint
+        {
+            public PointF Location { get; set; }
+            public DateTime From { get; set; }
+            public DateTime To { get; set; }
+        }
 
         private void LoadDeviceHistory(string deviceID)
         {
-            History.ItemsSource = null;
-            var movements = new ObservableCollection<GPSHistory>();
-            History.ItemsSource = movements;
+            
             new Client<GPSTrackerLocation>().Query(
                 new Filter<GPSTrackerLocation>(x => x.DeviceID).IsEqualTo(deviceID)
                     .And(x => x.Location.Timestamp).IsGreaterThanOrEqualTo(Date.Date.Date)
@@ -584,6 +523,8 @@ namespace PRSDesktop
                 new SortOrder<GPSTrackerLocation>(x => x.Location.Timestamp, SortDirection.Descending),
                 (table, error) =>
                 {
+                    
+                    var movements = new ObservableCollection<GPSHistory>();
                     if (table != null)
                     {
                         var updates = new List<GPSTrackerLocation>();
@@ -638,11 +579,49 @@ namespace PRSDesktop
                                 Latitude = 0.0F,
                                 Longitude = 0.0F
                             };
-                            Dispatcher.Invoke(() => { movements.Add(history); });
+                             movements.Add(history);
+                        }
+                    }
+                    
+                    List<WayPoint> waypoints = new List<WayPoint>();
+                    WayPoint? previous = null;
+                    foreach (var movement in movements.OrderBy(x=>x.Date))
+                    {
+                        var current = new PointF((float)movement.Longitude, (float)movement.Latitude);
+                        
+                        if (previous == null || Location.DistanceBetween(previous.Location, current, UnitOfLength.Kilometers) >= 0.05)
+                        {
+                            previous = new WayPoint()
+                            {
+                                Location = new PointF((float)movement.Longitude, (float)movement.Latitude),
+                                From = movement.Date,
+                                To = movement.Date
+                            };
+                            waypoints.Add(previous);
                         }
+                        else
+                            previous.To = movement.Date;
                     }
+
+                    Dispatcher.BeginInvoke(() =>
+                    {
+                        foreach (var waypoint in waypoints.Where(x=>x.To - x.From >= TimeSpan.FromMinutes(15)))
+                        {
+                            ViewModel.AddWayPoint(new MapMarker()
+                            {
+                                Label = $"{waypoint.From:h:mm}-{waypoint.To:h:mm}",
+                                Latitude = string.Format("{0:F6}", waypoint.Location.Y),
+                                Longitude = string.Format("{0:F6}", waypoint.Location.X),
+                            });
+                        }
+                        ViewModel.Refresh();
+                        History.ItemsSource = null;
+                        History.ItemsSource = movements;
+                    });
                 }
             );
+            
+
         }
 
         private bool Moved(List<CoreRow> rows, int row1, int row2)
@@ -716,12 +695,12 @@ namespace PRSDesktop
             //timer.Tick += ((t,e) => 
             //{
             //timer.IsEnabled = false;
-            viewmodel.Refresh();
+            ViewModel.Refresh();
 
             var layer = Map.Layers[0] as ImageryLayer;
             var nw = layer.GetLatLonFromPoint(new Point(0F, 0F));
             var se = layer.GetLatLonFromPoint(new Point(Map.ActualWidth, Map.ActualHeight));
-            var markers = viewmodel.Markers.Where(marker => ShowAll.IsChecked == true ? true : IsMarkerVisible(marker, nw, se))
+            var markers = ViewModel.Markers.Where(marker => ShowAll.IsChecked == true ? true : IsMarkerVisible(marker, nw, se))
                 .OrderBy(x => x.Description);
             Markers.ItemsSource = markers.ToArray();
             //});
@@ -741,6 +720,9 @@ namespace PRSDesktop
 
         private void ZoomToMarker(MapMarker marker)
         {
+            if (marker == null)
+                return;
+            
             if (double.Parse(marker.Latitude) != 0.0F && double.Parse(marker.Longitude) != 0.0F)
             {
                 var layer = Map.Layers[0] as ImageryLayer;
@@ -752,7 +734,7 @@ namespace PRSDesktop
         private void Markers_SelectionChanged(object sender, SelectionChangedEventArgs e)
         {
             var marker = Markers.SelectedItem as MapMarker;
-            viewmodel.Selected = Markers.SelectedItem as MapMarker;
+            ViewModel.Selected = Markers.SelectedItem as MapMarker;
 
             if (marker != null)
             {
@@ -820,7 +802,7 @@ namespace PRSDesktop
                 cur.Center = center;
                 cur.Radius = radius;
                 cur.MarkerTemplate = template;
-                cur.Markers = viewmodel.Markers;
+                cur.Markers = ViewModel.Markers;
                 cur.BingMapKey = "5kn7uPPBBGKdmWYiwHJL~LjUY7IIgFzGdXpkpWT7Fsw~AmlksNuYOBHNQ3cOa61Nz2ghvK5EbrBZ3aQqvTS4OjcPxTBGNGsj2hKc058CgtgJ";
             }
 
@@ -882,5 +864,15 @@ namespace PRSDesktop
             public string UpdatedBy { get; }
             public string DeviceID { get; }
         }
+
+        private void TabView_PageChanged(object sender, SelectionChangedEventArgs e)
+        {
+            ViewModel.View = TabView.SelectedIndex == 0
+                ? MapViewType.Current
+                : MapViewType.History;
+            // ImageryLayer.MarkerTemplate = TabView.SelectedIndex == 0
+            //     ? TryFindResource("MapMarkerTemplate") as DataTemplate
+            //     : TryFindResource("MapWayPointTemplate") as DataTemplate;
+        }
     }
 }

+ 13 - 0
prs.desktop/TestConverter.cs

@@ -0,0 +1,13 @@
+using System.Windows;
+using InABox.WPF;
+using Syncfusion.UI.Xaml.Maps;
+
+namespace PRSDesktop;
+
+public class TestConverter : UtilityConverter<CustomDataSymbol,string>
+{
+    public override string Convert(CustomDataSymbol value)
+    {
+        return (value?.Data as MapMarker)?.Label ?? "??";
+    }
+}