Browse Source

Interim changes to improve live maps

frogsoftware 1 year ago
parent
commit
b214669beb

+ 27 - 1
prs.classes/Settings/LiveMapsSettings.cs

@@ -1,10 +1,36 @@
-using InABox.Configuration;
+using System;
+using InABox.Configuration;
 
 namespace Comal.Classes
 {
+    
+    public enum LiveMapStyle
+    {
+        OSM,
+        BingStandard,
+        BingTerrain,
+        BingTerrainWithLabels,
+        GoogleStandard,
+        GoogleTerrain,
+        GoogleTerrainWithLabels
+    }
+    
     public class LiveMapsSettings : IUserConfigurationSettings
     {
+        [Obsolete("Replaced with EntityType", true)]
         public int ViewType { get; set; }
+
+        
+        [Obsolete("Replaced with EntityGroup", true)]
         public int ViewGroup { get; set; }
+                
+        public String? EntityType { get; set; }
+        
+        public Guid EntityGroup { get; set; }
+        
+        public LiveMapStyle MapStyle { get; set; }
+        
+        public String ApiKey { get; set; }
+        
     }
 }

+ 45 - 0
prs.desktop/Panels/Maps/LiveMapsImageryLayer.cs

@@ -0,0 +1,45 @@
+using System.Windows;
+using Comal.Classes;
+using Syncfusion.UI.Xaml.Maps;
+
+namespace PRSDesktop;
+
+public class LiveMapsImageryLayer : ImageryLayer
+{
+    
+    static readonly DependencyProperty StyleProperty =
+        DependencyProperty.Register(
+            nameof(Style),
+            typeof(LiveMapStyle),
+            typeof(MapViewModel),
+            new PropertyMetadata(LiveMapStyle.OSM)
+        );
+
+    public LiveMapStyle Style
+    {
+        get => (LiveMapStyle)GetValue(StyleProperty);
+        set
+        {
+            SetValue(StyleProperty, value);
+            LayerType = Style == LiveMapStyle.OSM
+                ? LayerType.OSM
+                : LayerType.Bing;
+            BingMapStyle = Style == LiveMapStyle.BingStandard
+                ? BingMapStyle.Road
+                : Style == LiveMapStyle.BingTerrain
+                    ? BingMapStyle.Aerial
+                    : BingMapStyle.AerialWithLabels;
+        }
+    }
+    
+    protected override string GetUri(int X, int Y, int Scale)
+    {
+        return Style == LiveMapStyle.GoogleStandard
+            ? $"http://mt1.google.com/vt/lyrs=m&x={X}&y={Y}&z={Scale}"
+            : Style == LiveMapStyle.GoogleTerrain
+                ? $"http://mt1.google.com/vt/lyrs=s&x={X}&y={Y}&z={Scale}"
+                : Style == LiveMapStyle.GoogleTerrainWithLabels
+                    ? $"http://mt1.google.com/vt/lyrs=y&x={X}&y={Y}&z={Scale}"
+                    : base.GetUri(X,Y,Scale);
+    }
+}

+ 2 - 1
prs.desktop/Panels/Maps/MapMarker.cs

@@ -38,5 +38,6 @@ public class MapMarker : DependencyObject
     public DateTime Updated { get; set; }
     public string UpdatedBy { get; set; }
     public string DeviceID { get; set; }
-    public Brush Background { get; internal set; }
+    
+    public bool Selected { get; set; }
 }

+ 477 - 50
prs.desktop/Panels/Maps/MapViewModel.cs

@@ -1,105 +1,532 @@
+using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Drawing;
+using System.Linq;
+using System.Linq.Expressions;
 using System.Windows;
-using System.Windows.Media;
-using InABox.WPF;
-using net.sf.mpxj;
+using System.Windows.Threading;
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Core;
+using Inflector;
 using Syncfusion.UI.Xaml.Maps;
+using Point = System.Windows.Point;
 
 namespace PRSDesktop;
 
-public enum MapViewType
+public enum LiveMapView
 {
-    Current,
-    History
+    All,
+    Selected
 }
 
 public class MapViewModel : DependencyObject
 {
     
-    private readonly List<MapMarker> _markers = new();
-    private readonly List<MapMarker> _waypoints = new();
-    private MapMarker? _selected;
+    static readonly DependencyProperty ViewProperty =
+        DependencyProperty.Register(
+            nameof(View),
+            typeof(LiveMapView),
+            typeof(MapViewModel),
+            new PropertyMetadata(LiveMapView.All)
+        );
 
-    public MapViewModel()
+    public LiveMapView View
     {
-        Markers = new ObservableCollection<MapMarker>();
-        WayPoints = new ObservableCollection<Point>();
+        get => (LiveMapView)GetValue(ViewProperty);
+        set
+        {
+            SetValue(ViewProperty, value);
+            Refresh();
+        }
     }
+    
+    static readonly DependencyProperty StyleProperty =
+        DependencyProperty.Register(
+            nameof(Style),
+            typeof(LiveMapStyle),
+            typeof(MapViewModel),
+            new PropertyMetadata(LiveMapStyle.OSM)
+        );
 
-    public ObservableCollection<MapMarker> Markers { get; }
-    public ObservableCollection<Point> WayPoints { get; }
+    public LiveMapStyle Style
+    {
+        get => (LiveMapStyle)GetValue(StyleProperty);
+        set => SetValue(StyleProperty, value);
+    }
     
-    static readonly DependencyProperty ViewProperty =
+    static readonly DependencyProperty ApiKeyProperty =
         DependencyProperty.Register(
-            nameof(View),
-            typeof(MapViewType),
-            typeof(MapViewModel));
+            nameof(ApiKey),
+            typeof(string),
+            typeof(MapViewModel),
+            new PropertyMetadata("")
+        );
+
+    public string ApiKey
+    {
+        get => (string)GetValue(ApiKeyProperty);
+        set => SetValue(ApiKeyProperty, value);
+    }
+    
+    static readonly DependencyProperty LayersProperty =
+        DependencyProperty.Register(
+            nameof(Layers),
+            typeof(ObservableCollection<ImageryLayer>),
+            typeof(MapViewModel),
+            new PropertyMetadata(new ObservableCollection<ImageryLayer>())
+        );
 
-    public MapViewType View
+    public ObservableCollection<ImageryLayer> Layers => (ObservableCollection<ImageryLayer>)GetValue(LayersProperty);
+    
+    static readonly DependencyProperty EntityTypesProperty =
+        DependencyProperty.Register(
+            nameof(EntityTypes),
+            typeof(Dictionary<Type, String>),
+            typeof(MapViewModel),
+            new PropertyMetadata(new Dictionary<Type, String>())
+        );
+    
+    public Dictionary<Type, String> EntityTypes => (Dictionary<Type, String>)GetValue(EntityTypesProperty);
+
+    static readonly DependencyProperty EntityTypeProperty =
+        DependencyProperty.Register(
+            nameof(EntityType),
+            typeof(Type),
+            typeof(MapViewModel)
+        );
+    
+    public Type? EntityType
     {
-        get => (MapViewType)GetValue(ViewProperty);
+        get => (Type) GetValue(EntityTypeProperty);
         set
         {
-            SetValue(ViewProperty, value);
-            Refresh();
+            SetValue(EntityTypesProperty, value);
+            ResetGroups();
         }
     }
+    
+    static readonly DependencyProperty EntityGroupsProperty =
+        DependencyProperty.Register(
+            nameof(EntityGroups),
+            typeof(Dictionary<Guid, String>),
+            typeof(MapViewModel),
+            new PropertyMetadata(new Dictionary<Guid, String>())
+        );
+    
+    public Dictionary<Guid, String> EntityGroups => (Dictionary<Guid, String>)GetValue(EntityGroupsProperty);
 
-    public MapMarker Selected
+    static readonly DependencyProperty EntityGroupProperty =
+        DependencyProperty.Register(
+            nameof(EntityGroup),
+            typeof(Guid),
+            typeof(MapViewModel),
+            new PropertyMetadata(Guid.Empty)
+        );
+    
+    public Guid EntityGroup
     {
-        get => _selected;
+        get => (Guid)GetValue(EntityGroupProperty);
         set
         {
-            _selected = value;
+            SetValue(EntityGroupProperty,value);
             Refresh();
         }
     }
-
-    public void AddWayPoint(MapMarker waypoint)
+    
+    //private readonly List<MapMarker> _markers = new();
+    //private readonly List<MapMarker> _waypoints = new();
+    //private MapMarker? _selected;
+    
+    
+    static readonly DependencyProperty MarkersProperty =
+        DependencyProperty.Register(
+            nameof(Markers),
+            typeof(ObservableCollection<MapMarker>),
+            typeof(MapViewModel),
+            new PropertyMetadata(new ObservableCollection<MapMarker>())
+        );
+    
+    public ObservableCollection<MapMarker> Markers => (ObservableCollection<MapMarker>)GetValue(MarkersProperty);
+    
+    
+    static readonly DependencyProperty SelectedMarkerProperty =
+        DependencyProperty.Register(
+            nameof(SelectedMarker),
+            typeof(MapMarker),
+            typeof(MapViewModel)
+        );
+    
+    public MapMarker? SelectedMarker
     {
-        _waypoints.Add(waypoint);
+        get => GetValue(SelectedMarkerProperty) as MapMarker;
+        set => SetValue(SelectedMarkerProperty, value);
     }
 
-    public void AddMarker(MapMarker marker, bool refresh = false)
+    static readonly DependencyProperty WayPointsProperty =
+        DependencyProperty.Register(
+            nameof(WayPoints),
+            typeof(ObservableCollection<PointF>),
+            typeof(MapViewModel),
+            new PropertyMetadata(new ObservableCollection<PointF>())
+        );
+    
+    public ObservableCollection<PointF> WayPoints => (ObservableCollection<PointF>)GetValue(WayPointsProperty);
+
+    private LiveMapsSettings? _settings;
+    
+    public void Setup(LiveMapsSettings settings)
     {
-        _markers.Add(marker);
-        if (refresh)
-            Refresh();
-    }
+        _settings = settings;
+        
+        LoadEntityTypes();
+        
+        EntityType = !String.IsNullOrWhiteSpace(_settings.EntityType)
+            ? EntityTypes.Any(x => String.Equals(x.Key.EntityName(), _settings.EntityType))
+                ? Type.GetType(_settings.EntityType)
+                : null
+            : null;
+        
+        EntityGroup = EntityGroups.Any(x => String.Equals(x.Key, _settings.EntityGroup))
+            ? _settings.EntityGroup
+            : Guid.Empty;
+
+        Style = _settings.MapStyle;
+        
+        ApiKey = _settings.ApiKey;
 
-    public void Clear()
+    }
+    
+    private void LoadEntityTypes()
     {
-        _selected = null;
-        _markers.Clear();
-        _waypoints.Clear();
-        Refresh();
+                
+        if (ClientFactory.IsSupported<Equipment>())
+            EntityTypes[typeof(Equipment)] = typeof(Equipment).GetCaption().Pluralize();
+
+        if (ClientFactory.IsSupported<Job>())
+            EntityTypes[typeof(Job)] = typeof(Job).GetCaption().Pluralize();
+
+        if (ClientFactory.IsSupported<TimeSheet>())
+            EntityTypes[typeof(TimeSheet)] = typeof(TimeSheet).GetCaption().Pluralize();
+
+        if (ClientFactory.IsSupported<GPSTracker>())
+            EntityTypes[typeof(GPSTracker)] = typeof(GPSTracker).GetCaption().Pluralize();
     }
 
+    private void ResetGroups()
+    {
+        EntityGroups.Clear();
+        
+        if (Type.Equals(EntityType,typeof(Equipment)))
+            LoadGroups<EquipmentGroup>("All Equipment", x => x.Description);
+        
+        else if (Type.Equals(EntityType,typeof(Job)))
+            LoadGroups<JobStatus>("All Jobs", x => x.Description);
+        
+        else if (Type.Equals(EntityType,typeof(GPSTracker)))
+            LoadGroups<GPSTrackerType>("All Trackers", x => x.Description);
+        
+        else if (Type.Equals(EntityType,typeof(TimeSheet)))
+        {
+            EntityGroups[Guid.Empty] = "Open TimeSheets";
+            EntityGroups[Guid.NewGuid()] = "TimeSheet Starts";
+            EntityGroups[CoreUtils.FullGuid] = "TimeSheet Finishes";
+        }
+    }
+    
+    private void LoadGroups<TGroup>(string all, Expression<Func<TGroup, string>> description)
+        where TGroup : Entity, IRemotable, IPersistent, new()
+    {
+        EntityGroups.Add(Guid.Empty, all);
+        var _groups = new Client<TGroup>().Query(
+            LookupFactory.DefineFilter<TGroup>(),
+            LookupFactory.DefineColumns<TGroup>(),
+            LookupFactory.DefineSort<TGroup>()
+        );
+        foreach (var _row in _groups.Rows)
+            EntityGroups[_row.Get<TGroup, Guid>(x => x.ID)] = _row.Get(description);
+    }
+    
     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)
+        if (View == LiveMapView.All)
+            LoadMarkers();
+        else if (SelectedMarker != null)
         {
+            LoadWayPoints();
             foreach (var waypoint in _waypoints)
             {
                 Markers.Add(waypoint);
                 WayPoints.Add(new Point(double.Parse(waypoint.Longitude), double.Parse(waypoint.Latitude)));
             }
         }
+    }
+
+    public void LoadMarkers()
+    {
+            if (EntityType != null)
+            {
+                if (EntityType.Equals("Equipment"))
+                {
+                    var filter = new Filter<Equipment>(x => x.TrackerLink.Location.Timestamp).IsNotEqualTo(DateTime.MinValue);
+                    if (!Security.IsAllowed<CanViewPrivateEquipment>())
+                        filter = filter.And(x => x.Private).IsEqualTo(false);
+                    if (EntityGroup != Guid.Empty)
+                        filter = filter.And(x => x.GroupLink.ID).IsEqualTo(EntityGroup);
+
+                    LoadMarkers(
+                        filter,
+                        x => x.ID,
+                        x => x.Code,
+                        x => x.Description,
+                        x => x.TrackerLink.DeviceID,
+                        x => x.TrackerLink.Location.Latitude,
+                        x => x.TrackerLink.Location.Longitude,
+                        x => x.TrackerLink.Description,
+                        x => x.TrackerLink.Location.Timestamp
+                    );
+                }
+
+                else if (EntityType.Equals("Jobs"))
+                {
+                    var filter = new Filter<Job>(x => x.SiteAddress.Location.Timestamp).IsNotEqualTo(DateTime.MinValue);
+                    if (EntityGroup != Guid.Empty)
+                        filter = filter.And(x => x.JobStatus.ID).IsEqualTo(EntityGroup);
+
+                    LoadMarkers(
+                        filter,
+                        x => x.ID,
+                        x => x.JobNumber,
+                        x => x.Name,
+                        null,
+                        x => x.SiteAddress.Location.Latitude,
+                        x => x.SiteAddress.Location.Longitude,
+                        x => x.LastUpdateBy,
+                        x => x.SiteAddress.Location.Timestamp
+                    );
+                }
+
+                else if (EntityType.Equals("TimeSheets"))
+                {
+                    if (EntityGroup == Guid.Empty) // Starts
+                        LoadMarkers(
+                            new Filter<TimeSheet>(x => x.Date).IsEqualTo(DateTime.Today).And(x => x.StartLocation.Timestamp)
+                                .IsNotEqualTo(new TimeSpan(0)),
+                            x => x.EmployeeLink.ID,
+                            x => x.EmployeeLink.Code,
+                            x => x.EmployeeLink.Name,
+                            null,
+                            x => x.StartLocation.Latitude,
+                            x => x.StartLocation.Longitude,
+                            x => x.SoftwareVersion,
+                            x => x.StartLocation.Timestamp
+                        );
+                    else if (EntityGroup == CoreUtils.FullGuid) // Finishes
+                        LoadMarkers(
+                            new Filter<TimeSheet>(x => x.Date).IsEqualTo(DateTime.Today).And(x => x.Finish).IsNotEqualTo(new TimeSpan(0))
+                                .And(x => x.FinishLocation.Timestamp).IsNotEqualTo(new TimeSpan(0)),
+                            x => x.EmployeeLink.ID,
+                            x => x.EmployeeLink.Code,
+                            x => x.EmployeeLink.Name,
+                            null,
+                            x => x.FinishLocation.Latitude,
+                            x => x.FinishLocation.Longitude,
+                            x => x.SoftwareVersion,
+                            x => x.FinishLocation.Timestamp
+                        );
+                    else // Open
+                        LoadMarkers(
+                            new Filter<TimeSheet>(x => x.Date).IsEqualTo(DateTime.Today).And(x => x.Finish).IsEqualTo(new TimeSpan(0))
+                                .And(x => x.StartLocation.Timestamp).IsNotEqualTo(new TimeSpan(0)),
+                            x => x.EmployeeLink.ID,
+                            x => x.EmployeeLink.Code,
+                            x => x.EmployeeLink.Name,
+                            null,
+                            x => x.StartLocation.Latitude,
+                            x => x.StartLocation.Longitude,
+                            x => x.SoftwareVersion,
+                            x => x.StartLocation.Timestamp
+                        );
+                }
 
+                else if (EntityType.Equals("Trackers"))
+                {
+                    var filter = new Filter<GPSTracker>(x => x.Location.Timestamp).IsNotEqualTo(DateTime.MinValue);
+                    if (EntityGroup != Guid.Empty)
+                        filter = filter.And(x => x.Type.ID).IsEqualTo(EntityGroup);
 
+                    LoadMarkers(
+                        filter,
+                        x => x.ID,
+                        x => x.DeviceID,
+                        x => x.Description,
+                        x => x.DeviceID,
+                        x => x.Location.Latitude,
+                        x => x.Location.Longitude,
+                        x => x.LastUpdateBy,
+                        x => x.Location.Timestamp
+                    );
+                }
+            }
+    }
+
+    private class DbMarker
+    {
+        public DbMarker(string code, string description, DateTime timestamp, double latitude, double longitude, string updatedby, string deviceid)
+        {
+            Code = code;
+            Description = description;
+            TimeStamp = timestamp;
+            Latitude = latitude;
+            Longitude = longitude;
+            UpdatedBy = updatedby;
+            DeviceID = deviceid;
+        }
+
+        public string Code { get; }
+        public string Description { get; }
+        public DateTime TimeStamp { get; }
+        public double Latitude { get; }
+        public double Longitude { get; }
+        public string UpdatedBy { get; }
+        public string DeviceID { get; }
     }
+    
+        private void LoadMarkers<T>(Filter<T> filter, Expression<Func<T, Guid>> guid, Expression<Func<T, string>>? code,
+            Expression<Func<T, string>> description, Expression<Func<T, string>>? deviceid, Expression<Func<T, double>> latitude,
+            Expression<Func<T, double>> longitude, Expression<Func<T, string>> updatedby, Expression<Func<T, DateTime>> timestamp, bool first = true)
+            where T : Entity, IRemotable, IPersistent, new()
+        {
+            var _columns = new Columns<T>(x=>x.ID)
+                .Add(guid)
+                .Add(description)
+                .Add(latitude)
+                .Add(longitude)
+                .Add(timestamp)
+                .Add(updatedby);
+
+            if (code != null)
+                _columns.Add(code);
+            
+            if (deviceid != null && !_columns.ColumnNames().Contains(CoreUtils.GetFullPropertyName(deviceid, ".")))
+                _columns.Add(deviceid);
+            
+            
+            new Client<T>().Query(
+                filter,
+                _columns,
+                null,
+                (table, error) =>
+                {
+                    Dispatcher.Invoke(() =>
+                    {
+                        if (error != null)
+                        {
+                            MessageBox.Show(error.Message);
+                        }
+                        else if (table != null)
+                        {
+                            var _values = new Dictionary<Guid, DbMarker>(); 
+                            
+                            var _groups = table.Rows
+                                .OrderBy(x=>x.Get(code ?? description))
+                                .ThenBy(x=>x.Get(timestamp))
+                                .Select(x => x.ToObject<T>())
+                                .GroupBy(x => x.ID);
+                            
+                            foreach (var _group in _groups)
+                            {
+                                var last = _group.OrderBy(timefunc).LastOrDefault();
+                                if (last != null)
+                                    Markers.Add(
+                                        new MapMarker
+                                        {
+                                            ID = last.ID,
+                                            Label = last.,
+                                            Latitude = string.Format("{0:F6}", _values[key].Latitude),
+                                            Longitude = string.Format("{0:F6}", _values[key].Longitude),
+                                            Description = _values[key].Description,
+                                            Updated = _values[key].TimeStamp,
+                                            UpdatedBy = _values[key].UpdatedBy,
+                                            DeviceID = _values[key].DeviceID
+                                        }
+                                    );
+
+                            }
+
+                            foreach (var _row in table.Rows)
+                            {
+                                var _id = _row.Get(guid);
+                                var _time = _row.Get(timestamp);
+                                if (!_values.ContainsKey(_id) || (first ? _values[_id].TimeStamp < _time : _values[_id].TimeStamp > _time))
+                                    _values[_id] = new DbMarker(
+                                        code != null ? _row.Get(code) : "",
+                                        _row.Get(description),
+                                        _time,
+                                        _row.Get(latitude),
+                                        _row.Get(longitude),
+                                        _row.Get(updatedby),
+                                        deviceid != null ? _row.Get(deviceid) : ""
+                                    );
+                            }
+
+                            foreach (var key in _values.Keys)
+                                Markers.Add(
+                                    new MapMarker
+                                    {
+                                        ID = key,
+                                        Label = _values[key].Code,
+                                        Latitude = string.Format("{0:F6}", _values[key].Latitude),
+                                        Longitude = string.Format("{0:F6}", _values[key].Longitude),
+                                        Description = _values[key].Description,
+                                        Updated = _values[key].TimeStamp,
+                                        UpdatedBy = _values[key].UpdatedBy,
+                                        DeviceID = _values[key].DeviceID
+                                    }
+                                );
+                        }
+
+                        ResetZoom();
+
+                        LoadMarkerList();
+                    });
+                }
+            );
+        }
+
+        private void ResetZoom()
+        {
+            var nwLon = double.MaxValue;
+            var nwLat = double.MinValue;
+            var seLon = double.MinValue;
+            var seLat = double.MaxValue;
+
+            foreach (var marker in _markers)
+            {
+                var lat = double.Parse(marker.Latitude);
+                var lon = double.Parse(marker.Longitude);
+                if (lat != 0.0F && lon != 0.00)
+                {
+                    nwLat = lat > nwLat ? lat : nwLat;
+                    seLat = lat < seLat ? lat : seLat;
+                    nwLon = lon < nwLon ? lon : nwLon;
+                    seLon = lon > seLon ? lon : seLon;
+                }
+            }
+
+            
+            var cLat = (nwLat + seLat) / 2.0F;
+            var cLon = (nwLon + seLon) / 2.0F;
+            Center = new Point(cLat, cLon);
+            var nw = new Location { Latitude = nwLat, Longitude = nwLon };
+            var c = new Location { Latitude = cLat, Longitude = cLon };
+            Radius = c.DistanceTo(nw, UnitOfLength.Kilometers) / 2.0F;
+        }
+        
+        public double Radius { get; set; }
+        public Point Center { get; set; }
+        
 }

+ 34 - 27
prs.desktop/Panels/Maps/MapsPanel.xaml

@@ -6,6 +6,7 @@
              xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
              xmlns:dynamicGrid="clr-namespace:InABox.DynamicGrid;assembly=InABox.Wpf"
              xmlns:prsDesktop="clr-namespace:PRSDesktop"
+             xmlns:wpf="clr-namespace:InABox.WPF;assembly=InABox.Wpf"
              mc:Ignorable="d"
              d:DesignHeight="450" d:DesignWidth="800">
     
@@ -15,22 +16,22 @@
     
     <UserControl.Resources>
         <prsDesktop:TestConverter x:Key="TestConverter" />
+        <wpf:BooleanToBrushConverter x:Key="BooleanToBrushConverter" TrueValue="Yellow" FalseValue="Orange" />
         <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}}"
+                    Background="{Binding Data.Selected, Converter={StaticResource BooleanToBrushConverter}}"
                     Padding="5,2,5,2"
                     PreviewMouseDown="Image_PreviewMouseDown">
                 <Label
-                    Content="{Binding ., Converter={StaticResource TestConverter}}"
+                    Content="{Binding Data.Description}"
                     HorizontalContentAlignment="Center"
                     VerticalContentAlignment="Center"
                     FontSize="10"
-                    Background="Red"
-                    Foreground="Yellow"/>
+                    Foreground="Black"/>
             </Border>
         </DataTemplate>
         <!-- -->
@@ -53,6 +54,7 @@
         <!--     </Border> -->
         <!-- </DataTemplate> -->
     </UserControl.Resources>
+    
     <Grid>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="Auto" />
@@ -67,31 +69,36 @@
         <Border Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="0,0,2,0" BorderBrush="Gray"
                 BorderThickness="0.75" Padding="5">
             <Slider x:Name="Zoom" Orientation="Vertical" ValueChanged="Zoom_ValueChanged" Minimum="1" Maximum="20"
-                    Value="11" />
+                    Value="{Binding Zoom, Mode=TwoWay}" />
         </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 
+            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"
+                <prsDesktop:LiveMapsImageryLayer
+                    Style="{Binding Style}"
+                    BingMapKey="{Binding ApiKey}"
+                    Center="{Binding Center}"
+                    Radius="{Binding Radius}"
                     Markers="{Binding Markers}"
+                    DistanceType="KiloMeter"
                     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:ImageryLayer.SubShapeFileLayers>
+                        <syncfusion:SubShapeFileLayer >
+                            <syncfusion:SubShapeFileLayer.MapElements >
+                                <syncfusion:MapPolyline Stroke="DarkBlue" StrokeThickness="5" Points="{Binding WayPoints}" />
+                            </syncfusion:SubShapeFileLayer.MapElements>
+                        </syncfusion:SubShapeFileLayer>
+                    </syncfusion:ImageryLayer.SubShapeFileLayers>
+                </prsDesktop:LiveMapsImageryLayer>
             </syncfusion:SfMap.Layers>
         </syncfusion:SfMap>
         
@@ -100,15 +107,15 @@
             <StackPanel Orientation="Vertical">
                 <Label Content="Map Options" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
                 <ComboBox x:Name="Viewstyle" Margin="5,0,5,5" SelectedIndex="0"
-                          SelectionChanged="Viewstyle_SelectionChanged" Visibility="Collapsed">
+                          SelectionChanged="Viewstyle_SelectionChanged" Visibility="Collapsed" >
                     <ComboBoxItem x:Name="OSMViewStyle" Content="Open Street Map" />
                     <ComboBoxItem x:Name="BingStandardViewStyle" Content="Bing Standard View" />
                     <ComboBoxItem x:Name="BingTerrainViewStyle" Content="Bing Terrain View" />
                     <ComboBoxItem x:Name="BingTerrainLabelsViewStyle" Content="Bing Terrain with Labels" />
                     <ComboBoxItem x:Name="GoogleMapsViewStyle" Content="Google Maps" />
                 </ComboBox>
-                <ComboBox x:Name="ViewType" Margin="5,0,5,5" Visibility="Visible" />
-                <ComboBox x:Name="ViewGroup" Margin="5,0,5,5" DisplayMemberPath="Value" SelectedValuePath="Key" />
+                <ComboBox x:Name="ViewType" Margin="5,0,5,5" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding EntityType, Mode=TwoWay}" />
+                <ComboBox x:Name="ViewGroup" Margin="5,0,5,5" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding EntityGroup, Mode=TwoWay}" />
             </StackPanel>
         </Border>
 

+ 15 - 266
prs.desktop/Panels/Maps/MapsPanel.xaml.cs

@@ -19,15 +19,7 @@ using Point = System.Windows.Point;
 
 namespace PRSDesktop
 {
-    public class ImageryLayerExt : ImageryLayer
-    {
-        protected override string GetUri(int X, int Y, int Scale)
-        {
-            var link = "http://mt1.google.com/vt/lyrs=y&x=" + X + "&y=" + Y + "&z=" + Scale;
-            return link;
-        }
-    }
-
+    
     public class GPSHistory
     {
         public DateTime Date { get; set; }
@@ -50,7 +42,6 @@ namespace PRSDesktop
         public MapsPanel()
         {
             InitializeComponent();
-            //ausgov.Uri = "c:\\development\\maps\\MB_2011_WA.shp";
         }
 
         public bool IsReady { get; set; }
@@ -72,113 +63,7 @@ namespace PRSDesktop
 
         public void Refresh()
         {
-            var type = ViewType.SelectedItem as string;
-            if (!string.IsNullOrWhiteSpace(type))
-            {
-                var grpid = ((KeyValuePair<Guid, string>)ViewGroup.SelectedItem).Key;
-
-                if (type.Equals("Equipment"))
-                {
-                    var filter = new Filter<Equipment>(x => x.TrackerLink.Location.Timestamp).IsNotEqualTo(DateTime.MinValue);
-                    if (!Security.IsAllowed<CanViewPrivateEquipment>())
-                        filter = filter.And(x => x.Private).IsEqualTo(false);
-                    if (grpid != Guid.Empty)
-                        filter = filter.And(x => x.GroupLink.ID).IsEqualTo(grpid);
-
-                    LoadMarkers(
-                        filter,
-                        x => x.ID,
-                        x => x.Code,
-                        x => x.Description,
-                        x => x.TrackerLink.DeviceID,
-                        x => x.TrackerLink.Location.Latitude,
-                        x => x.TrackerLink.Location.Longitude,
-                        x => x.TrackerLink.Description,
-                        x => x.TrackerLink.Location.Timestamp
-                    );
-                }
-
-                else if (type.Equals("Jobs"))
-                {
-                    var filter = new Filter<Job>(x => x.SiteAddress.Location.Timestamp).IsNotEqualTo(DateTime.MinValue);
-                    if (grpid != Guid.Empty)
-                        filter = filter.And(x => x.JobStatus.ID).IsEqualTo(grpid);
-
-                    LoadMarkers(
-                        filter,
-                        x => x.ID,
-                        x => x.JobNumber,
-                        x => x.Name,
-                        null,
-                        x => x.SiteAddress.Location.Latitude,
-                        x => x.SiteAddress.Location.Longitude,
-                        x => x.LastUpdateBy,
-                        x => x.SiteAddress.Location.Timestamp
-                    );
-                }
-
-                else if (type.Equals("TimeSheets"))
-                {
-                    if (grpid == Guid.Empty) // Starts
-                        LoadMarkers(
-                            new Filter<TimeSheet>(x => x.Date).IsEqualTo(DateTime.Today).And(x => x.StartLocation.Timestamp)
-                                .IsNotEqualTo(new TimeSpan(0)),
-                            x => x.EmployeeLink.ID,
-                            x => x.EmployeeLink.Code,
-                            x => x.EmployeeLink.Name,
-                            null,
-                            x => x.StartLocation.Latitude,
-                            x => x.StartLocation.Longitude,
-                            x => x.SoftwareVersion,
-                            x => x.StartLocation.Timestamp
-                        );
-                    else if (grpid == CoreUtils.FullGuid) // Finishes
-                        LoadMarkers(
-                            new Filter<TimeSheet>(x => x.Date).IsEqualTo(DateTime.Today).And(x => x.Finish).IsNotEqualTo(new TimeSpan(0))
-                                .And(x => x.FinishLocation.Timestamp).IsNotEqualTo(new TimeSpan(0)),
-                            x => x.EmployeeLink.ID,
-                            x => x.EmployeeLink.Code,
-                            x => x.EmployeeLink.Name,
-                            null,
-                            x => x.FinishLocation.Latitude,
-                            x => x.FinishLocation.Longitude,
-                            x => x.SoftwareVersion,
-                            x => x.FinishLocation.Timestamp
-                        );
-                    else // Open
-                        LoadMarkers(
-                            new Filter<TimeSheet>(x => x.Date).IsEqualTo(DateTime.Today).And(x => x.Finish).IsEqualTo(new TimeSpan(0))
-                                .And(x => x.StartLocation.Timestamp).IsNotEqualTo(new TimeSpan(0)),
-                            x => x.EmployeeLink.ID,
-                            x => x.EmployeeLink.Code,
-                            x => x.EmployeeLink.Name,
-                            null,
-                            x => x.StartLocation.Latitude,
-                            x => x.StartLocation.Longitude,
-                            x => x.SoftwareVersion,
-                            x => x.StartLocation.Timestamp
-                        );
-                }
-
-                else if (type.Equals("Trackers"))
-                {
-                    var filter = new Filter<GPSTracker>(x => x.Location.Timestamp).IsNotEqualTo(DateTime.MinValue);
-                    if (grpid != Guid.Empty)
-                        filter = filter.And(x => x.Type.ID).IsEqualTo(grpid);
-
-                    LoadMarkers(
-                        filter,
-                        x => x.ID,
-                        x => x.DeviceID,
-                        x => x.Description,
-                        x => x.DeviceID,
-                        x => x.Location.Latitude,
-                        x => x.Location.Longitude,
-                        x => x.LastUpdateBy,
-                        x => x.Location.Timestamp
-                    );
-                }
-            }
+            ViewModel.Refresh();
         }
 
         public Dictionary<string, object[]> Selected()
@@ -190,20 +75,12 @@ namespace PRSDesktop
         {
             _settings = new UserConfiguration<LiveMapsSettings>().Load();
 
-            if (ClientFactory.IsSupported<Equipment>())
-                ViewType.Items.Add("Equipment");
-
-            if (ClientFactory.IsSupported<Job>())
-                ViewType.Items.Add("Jobs");
-
-            if (ClientFactory.IsSupported<TimeSheet>())
-                ViewType.Items.Add("TimeSheets");
-
-            if (ClientFactory.IsSupported<GPSTracker>())
-                ViewType.Items.Add("Trackers");
-
-            ViewType.SelectedIndex = ViewType.Items.Count > _settings.ViewType ? _settings.ViewType : -1;
+            ViewModel.Setup()
+            
+            ViewModel.
             ViewType.SelectionChanged += ViewTypeSelectionChanged;
+            
+            ViewModel.EntityGroup = _settings.EntityGroup
 
             var groups = ViewType.SelectedIndex > -1 ? LoadGroups(ViewType.SelectedItem as string) : LoadGroups("");
             ViewGroup.ItemsSource = groups;
@@ -223,19 +100,7 @@ namespace PRSDesktop
             return new Dictionary<Type, CoreTable>();
         }
 
-        private void LoadGroups<TGroup>(string all, Dictionary<Guid, string> results, Expression<Func<TGroup, string>> description)
-            where TGroup : Entity, IRemotable, IPersistent, new()
-        {
-            results.Clear();
-            results.Add(Guid.Empty, all);
-            var groups = new Client<TGroup>().Query(
-                LookupFactory.DefineFilter<TGroup>(),
-                LookupFactory.DefineColumns<TGroup>(),
-                LookupFactory.DefineSort<TGroup>()
-            );
-            foreach (var row in groups.Rows)
-                results[row.Get<TGroup, Guid>(x => x.ID)] = row.Get(description);
-        }
+
 
         private void ViewTypeSelectionChanged(object sender, SelectionChangedEventArgs e)
         {
@@ -250,34 +115,7 @@ namespace PRSDesktop
             ViewGroup.SelectedIndex = groups.Any() ? 0 : -1;
         }
 
-        private Dictionary<Guid, string> LoadGroups(string sel)
-        {
-            var result = new Dictionary<Guid, string>();
-
-            if (sel.Equals("Equipment"))
-            {
-                LoadGroups<EquipmentGroup>("All Equipment", result, x => x.Description);
-            }
-
-            else if (sel.Equals("Jobs"))
-            {
-                LoadGroups<JobStatus>("All Jobs", result, x => x.Description);
-            }
-
-            else if (sel.Equals("TimeSheets"))
-            {
-                result[Guid.NewGuid()] = "Open TimeSheets";
-                result[Guid.Empty] = "TimeSheet Starts";
-                result[CoreUtils.FullGuid] = "TimeSheet Finishes";
-            }
-
-            else if (sel.Equals("Trackers"))
-            {
-                LoadGroups<GPSTrackerType>("All Trackers", result, x => x.Description);
-            }
-
-            return result;
-        }
+        
 
         private void ViewGroupSelectionChanged(object sender, SelectionChangedEventArgs e)
         {
@@ -292,75 +130,6 @@ namespace PRSDesktop
             ViewModel.Markers.Clear();
         }
 
-        private void LoadMarkers<T>(Filter<T> filter, Expression<Func<T, object>> guid, Expression<Func<T, object>> code,
-            Expression<Func<T, object>> description, Expression<Func<T, object>> deviceid, Expression<Func<T, object>> latitude,
-            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();
-            var columns = new Columns<T>(guid, code, description, latitude, longitude, timestamp, updatedby);
-            if (deviceid != null && !columns.ColumnNames().Contains(CoreUtils.GetFullPropertyName(deviceid, ".")))
-                columns.Add(deviceid);
-
-            new Client<T>().Query(
-                filter,
-                columns,
-                null,
-                (table, error) =>
-                {
-                    Dispatcher.Invoke(() =>
-                    {
-                        ViewModel.Markers.Clear();
-                        if (error != null)
-                        {
-                            MessageBox.Show(error.Message);
-                        }
-                        else
-                        {
-                            var values = new Dictionary<Guid, DbMarker>();
-                            foreach (var row in table.Rows)
-                            {
-                                var id = (Guid)row.Get(guid);
-                                var time = (DateTime)row.Get(timestamp);
-                                if (!values.ContainsKey(id) || (first ? values[id].TimeStamp < time : values[id].TimeStamp > time))
-                                    values[id] = new DbMarker(
-                                        code != null ? (string)row.Get(code) : "",
-                                        (string)row.Get(description),
-                                        time,
-                                        (double)row.Get(latitude),
-                                        (double)row.Get(longitude),
-                                        (string)row.Get(updatedby),
-                                        deviceid != null ? (string)row.Get(deviceid) : ""
-                                    );
-                            }
-
-                            foreach (var key in values.Keys)
-                                ViewModel.AddMarker(
-                                    new MapMarker
-                                    {
-                                        ID = key,
-                                        Label = values[key].Code,
-                                        Latitude = string.Format("{0:F6}", values[key].Latitude),
-                                        Longitude = string.Format("{0:F6}", values[key].Longitude),
-                                        Description = values[key].Description,
-                                        Updated = values[key].TimeStamp,
-                                        UpdatedBy = values[key].UpdatedBy,
-                                        DeviceID = values[key].DeviceID
-                                    }
-                                );
-                            ViewModel.Refresh();
-                        }
-
-                        //if (bFirst)
-                        ResetZoom();
-                        bFirst = false;
-
-                        LoadMarkerList();
-                    });
-                }
-            );
-        }
-
         private void ResetZoom()
         {
             var nwLon = double.MaxValue;
@@ -779,7 +548,7 @@ namespace PRSDesktop
             }
             else if (Viewstyle.SelectedItem == GoogleMapsViewStyle)
             {
-                layer = typeof(ImageryLayerExt);
+                layer = typeof(GoogleImageryLayer);
             }
             else
             {
@@ -796,8 +565,8 @@ namespace PRSDesktop
                 Map.Layers.Clear();
                 if (layer == typeof(ImageryLayer))
                     Map.Layers.Add(new ImageryLayer());
-                else if (layer == typeof(ImageryLayerExt))
-                    Map.Layers.Add(new ImageryLayerExt());
+                else if (layer == typeof(GoogleImageryLayer))
+                    Map.Layers.Add(new GoogleImageryLayer());
                 cur = Map.Layers[0] as ImageryLayer;
                 cur.Center = center;
                 cur.Radius = radius;
@@ -843,33 +612,13 @@ namespace PRSDesktop
             LoadMarkerList();
         }
 
-        private class DbMarker
-        {
-            public DbMarker(string code, string description, DateTime timestamp, double latitude, double longitude, string updatedby, string deviceid)
-            {
-                Code = code;
-                Description = description;
-                TimeStamp = timestamp;
-                Latitude = latitude;
-                Longitude = longitude;
-                UpdatedBy = updatedby;
-                DeviceID = deviceid;
-            }
-
-            public string Code { get; }
-            public string Description { get; }
-            public DateTime TimeStamp { get; }
-            public double Latitude { get; }
-            public double Longitude { get; }
-            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;
+                ? LiveMapView.All
+                : LiveMapView.Selected;
             // ImageryLayer.MarkerTemplate = TabView.SelectedIndex == 0
             //     ? TryFindResource("MapMarkerTemplate") as DataTemplate
             //     : TryFindResource("MapWayPointTemplate") as DataTemplate;