using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Input; using System.Windows.Media.Imaging; using Comal.Classes; using ExCSS; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.WPF; using Syncfusion.UI.Xaml.Diagram; using Syncfusion.UI.Xaml.Maps; using GeoFence = Comal.Classes.GeoFence; using Point = System.Windows.Point; namespace PRSDesktop; public class MapMarker { public string Name { get; set; } = "Hi There"; public string Label { get; set; } = ""; public string Longitude { get; set; } public string Latitude { get; set; } } public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged { private EquipmentGroup[]? _groups; private Equipment[]? _items; private EquipmentGroup? _selectedGroup; private string? _search; private DateTime _date = DateTime.Today; private Equipment? _selectedItem; private ObservableCollection? _waypoints; private Point _center; private double _radius; private GeoFence[]? _geofences; private ObservableCollection _elements; private ObservableCollection? _sites; public EquipmentGroup[]? Groups { get => _groups; set => SetField(ref _groups, value); } public EquipmentGroup? SelectedGroup { get => _selectedGroup; set { SetField(ref _selectedGroup, value); SelectedItem = null; SaveSettings(); OnPropertyChanged(nameof(Visible)); } } public Equipment[]? Items { get => _items; set => SetField(ref _items, value); } public GeoFence[]? GeoFences { get => _geofences; set { _fencesMap.Clear(); if (value is not null) { foreach (var fence in value ?? []) _fencesMap[fence] = Serialization.Deserialize(fence.Geofence) ?? new GeoFenceDefinition(); } SetField(ref _geofences, value); } } private Dictionary _fencesMap = new(); public string? Search { get => _search; set { SetField(ref _search, value); OnPropertyChanged(nameof(Visible)); } } public Equipment[]? Visible => Items?.Where(x => x.GroupLink.ID == (SelectedGroup?.ID ?? CoreUtils.FullGuid) && ( string.IsNullOrEmpty(Search) || x.Code.Contains(Search,StringComparison.CurrentCultureIgnoreCase) || x.Description.Contains(Search,StringComparison.CurrentCultureIgnoreCase) ) ).ToArray(); public DateTime Date { get => _date; set { SetField(ref _date, value); ReloadLocations(); OnPropertyChanged(nameof(Visible)); } } public Equipment? SelectedItem { get => _selectedItem; set { SetField(ref _selectedItem, value); ReloadLocations(); } } private void CenterMap() { var nwLon = double.MaxValue; var nwLat = double.MinValue; var seLon = double.MinValue; var seLat = double.MaxValue; var points = new List(); if (_waypoints != null) points.AddRange(_waypoints); if (_geofences != null) { foreach (var geofence in _fencesMap.Where(x=>x.Key.Type == GPSLocationType.Office)) points.AddRange(geofence.Value.Coordinates.Select(x => new Point(x.Latitude, x.Longitude))); } foreach (var point in points) { var lat = point.X; var lon = point.Y; 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(-33.0 + new Random(DateTime.Now.Millisecond).NextDouble(), 115.0 + new Random(DateTime.Now.Millisecond).NextDouble()); Center = new Point(cLat, cLon); var c = new Location { Latitude = cLat, Longitude = cLon }; var nw = new Location { Latitude = nwLat, Longitude = nwLon }; Radius = Math.Max(1.0, c.DistanceTo(nw, UnitOfLength.Kilometers) / 2.0F); } private void ReloadLocations() { var trackerids = Visible?.Select(x => x.TrackerLink.ID).ToArray() ?? []; Client.Query( new Filter(x=>x.Tracker.ID).InList(trackerids) .And(x => x.Location.Timestamp).IsGreaterThanOrEqualTo(Date.Date) .And(x => x.Location.Timestamp).IsLessThan(Date.Date.AddDays(1)), Columns.None() .Add(x=>x.Tracker.ID) .Add(x=>x.Location.Latitude) .Add(x=>x.Location.Longitude) .Add(x=>x.Location.Address) .Add(x=>x.Location.Timestamp), new SortOrder(x=>x.Location.Timestamp,SortDirection.Ascending), (data, _) => { Dispatcher.BeginInvoke(() => { Point[]? others = null; if (data is null) { EquipmentColorConverter.Pings = []; Waypoints = null; Sites = null; } else { EquipmentColorConverter.Pings = data.ToArray(); others = data.Rows .Where(r => r.Get(c=>c.Tracker.ID) != (SelectedItem?.TrackerLink.ID ?? CoreUtils.FullGuid)) .Select(r => new Point() { X = r.Get(c => c.Location.Latitude), Y = r.Get(c => c.Location.Longitude) } ).ToArray(); var locations = data.Rows .Where(r => r.Get(c=>c.Tracker.ID) == (SelectedItem?.TrackerLink.ID ?? CoreUtils.FullGuid)) .Select(r => new Point( r.Get(c => c.Location.Latitude), r.Get(c => c.Location.Longitude) ) )?.ToArray() ?? []; Waypoints = new ObservableCollection(locations); Dictionary sites = new Dictionary(); GeoFence? curFence = null; foreach (var row in data.Rows.Where(r => r.Get(c=>c.Tracker.ID) == (SelectedItem?.TrackerLink.ID ?? CoreUtils.FullGuid))) { var time = $"{row.Get(c => c.Location.Timestamp):h:mm tt}"; var geopoint = new GeoPoint(row.Get(c => c.Location.Latitude), row.Get(c => c.Location.Longitude)); bool bFound = false; foreach (var geofence in _fencesMap) { if (geofence.Value.Contains(geopoint)) { if (!sites.ContainsKey(geofence.Key)) sites[geofence.Key] = new MapMarker() { Latitude = $"{geopoint.Latitude}", Longitude = $"{geopoint.Longitude}" }; var timelist = sites[geofence.Key].Label.Split('\n').Where(x=>!string.IsNullOrWhiteSpace(x)).ToList(); var first = timelist.FirstOrDefault() ?? ""; if (!string.Equals(first,geofence.Key.Name)) timelist.Insert(0,geofence.Key.Name); if ((geofence.Key == curFence) && timelist.Any()) { var lasttime = timelist.Last().Split(" - ").ToList(); timelist.Remove(timelist.Last()); timelist.Add($"{lasttime.First()} - {time}"); } else timelist.Add($"{time} - {time}"); sites[geofence.Key].Label = string.Join("\n", timelist); curFence = geofence.Key; bFound = true; break; } } if (!bFound) curFence = null; } Sites = new ObservableCollection(sites.Values); } OnPropertyChanged(nameof(Visible)); RecalculateLayers(others); CenterMap(); }); } ); } public ObservableCollection Elements { get => _elements; set => SetField(ref _elements, value); } private void RecalculateLayers(Point[]? markers) { var elements = new ObservableCollection(); if (_geofences?.Any() == true) { foreach (var geofence in _geofences) { var definition = Serialization.Deserialize(geofence.Geofence) ?? new GeoFenceDefinition(); var polygon = new MapPolygon() { Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.LightSalmon) { Opacity = 0.5 }, Stroke = System.Windows.Media.Brushes.Firebrick, StrokeThickness = 0.75, Points = new ObservableCollection( definition.Coordinates.Select(x => new Point(x.Latitude, x.Longitude))), }; elements.Add(polygon); } } if (markers?.Any() == true) { foreach (var marker in markers.Distinct()) { var circle = new MapCircle() { Center = marker, Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Magenta) { Opacity = 0.5 }, Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Black), StrokeThickness = 0, Radius = 10 }; elements.Add(circle); } } if (_waypoints?.Any( ) == true) { var line = new MapPolyline() { Points = new ObservableCollection(new ObservableCollection(_waypoints)), Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Navy) { Opacity = 0.5 }, Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Navy) { Opacity = 0.5 }, StrokeThickness = 4 }; elements.Add(line); } Elements = elements; } public ICommand RefreshCommand { get; set; } public ObservableCollection? Sites { get => _sites; set => SetField(ref _sites, value); } public ObservableCollection? Waypoints { get => _waypoints; set => SetField(ref _waypoints, value); } public Point Center { get => _center; set => SetField(ref _center, value); } public double Radius { get => _radius; set => SetField(ref _radius, value); } public LiveMapsSettings Settings { get; private set; } public LiveMapsPanelViewModel() { Settings = new UserConfiguration().Load(); } public void SaveSettings() { Settings.GroupID = _selectedGroup?.ID ?? Guid.Empty; Settings.ItemID = _selectedItem?.ID ?? Guid.Empty; new UserConfiguration().Save(Settings); } public void Refresh() { MultiQuery query = new MultiQuery(); query.Add(new Filter().All()); query.Add(new Filter().All(), Columns.None() .Add(x => x.ID) .Add(x => x.Description) .Add(x=>x.Thumbnail.ID) ); query.Add( new Filter(x=>x.ID).InQuery(new Filter().All(),x=>x.Thumbnail.ID)); query.Add( new Filter(x => x.TrackerLink.ID).IsNotEqualTo(Guid.Empty), Columns.None() .Add(x => x.ID) .Add(x => x.Code) .Add(x => x.Description) .Add(x=>x.GroupLink.ID) .Add(x=>x.GroupLink.Thumbnail.ID) .Add(x=>x.TrackerLink.ID) .Add(x=>x.TrackerLink.Location.Timestamp) .Add(x=>x.TrackerLink.DeviceID) .Add(x=>x.TrackerLink.BatteryLevel) ); query.Query( _ => { Dispatcher.BeginInvoke(() => { foreach (var row in query.Get().Rows) { var img = ImageUtils.LoadImage(row.Get(x => x.Data)); EquipmentThumbnailConverter.Cache[row.Get(x => x.ID)] = img; } // EquipmentThumbnailConverter.Cache = query.Get() // .ToDictionary(x => x.ID, // x => ImageUtils.LoadImage(x.Data)); GeoFences = query.Get().ToArray(); Groups = query.Get().ToArray(); SelectedGroup = _groups?.FirstOrDefault(x=>x.ID == _selectedGroup?.ID); Items = query.Get().ToArray(); SelectedItem = _items?.FirstOrDefault(x=>x.ID == _selectedItem?.ID); }); }); } public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) { if (EqualityComparer.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } }