EquipmentMapsViewModel.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using Avalonia;
  2. using Comal.Classes;
  3. using CommunityToolkit.Mvvm.ComponentModel;
  4. using CommunityToolkit.Mvvm.Input;
  5. using InABox.Avalonia;
  6. using InABox.Avalonia.Components;
  7. using InABox.Configuration;
  8. using InABox.Core;
  9. using PRS.Avalonia.Dialogs;
  10. using ReactiveUI;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Collections.ObjectModel;
  14. using System.Linq;
  15. using System.Threading.Tasks;
  16. using static PRS.Avalonia.Modules.EquipmentMapsView;
  17. namespace PRS.Avalonia.Modules;
  18. public class EquipmentMapsSettings : ILocalConfigurationSettings
  19. {
  20. public Guid[] SelectedCategories { get; set; } = [];
  21. }
  22. public partial class EquipmentMapsViewModel : ModuleViewModel
  23. {
  24. public override string Title => "Live Maps";
  25. [ObservableProperty]
  26. private string _searchText = "";
  27. private readonly EquipmentMapsSettings _settings;
  28. [ObservableProperty]
  29. private ObservableCollection<Marker> _jobMarkers = new();
  30. [ObservableProperty]
  31. private ObservableCollection<Marker> _equipmentMarkers = new();
  32. [ObservableProperty]
  33. private JobModel _jobs;
  34. [ObservableProperty]
  35. private EquipmentModel _equipment;
  36. [ObservableProperty]
  37. private Point _coordinates;
  38. [ObservableProperty]
  39. private int _zoomLevel;
  40. public EquipmentMapsViewModel()
  41. {
  42. _settings = new LocalConfiguration<EquipmentMapsSettings>().Load();
  43. Jobs = new JobModel(
  44. DataAccess,
  45. () => Repositories.JobFilter(),
  46. () => DefaultCacheFileName<JobShell>());
  47. Equipment = new EquipmentModel(
  48. DataAccess,
  49. () => new Filter<Equipment>().All(),
  50. () => DefaultCacheFileName<EquipmentShell>());
  51. PrimaryMenu.Add(new AvaloniaMenuItem(Images.menu, SelectFilter));
  52. }
  53. protected override async Task<TimeSpan> OnRefresh()
  54. {
  55. await Task.WhenAll(
  56. Jobs.RefreshAsync(false),
  57. Equipment.RefreshAsync(false));
  58. Refresh();
  59. return TimeSpan.Zero;
  60. }
  61. private void Refresh()
  62. {
  63. var jobs = Jobs.Items.Where(FilterJob)
  64. .Select(x => new EquipmentMapsView.Marker(x.JobNumber, x.Location.Latitude, x.Location.Longitude))
  65. .ToArray();
  66. var equipment = Equipment.Items.Where(FilterEquipment)
  67. .Select(x => new EquipmentMapsView.Marker(x.Code, x.Latitude, x.Longitude))
  68. .ToArray();
  69. CenterAndZoom(jobs.Concat(equipment).Select(x => x.Coordinates).ToArray());
  70. JobMarkers = new ObservableCollection<Marker>(jobs);
  71. EquipmentMarkers = new ObservableCollection<Marker>(equipment);
  72. }
  73. private void CenterAndZoom(Point[] points)
  74. {
  75. points = points.Where(x => x != default).ToArray();
  76. Point coords;
  77. int zoom = 15;
  78. if (points.Length == 0)
  79. {
  80. if(App.GPS is not null)
  81. {
  82. coords = new Point(App.GPS.Latitude, App.GPS.Longitude);
  83. }
  84. else
  85. {
  86. return;
  87. }
  88. }
  89. else if (points.Length == 1)
  90. {
  91. coords = new Point(points[0].Y, points[0].X);
  92. }
  93. else
  94. {
  95. var latitudes = points.Select(x => x.Y).OrderBy(x => x).ToList();
  96. var longitudes = points.Select(x => x.X).OrderBy(x => x).ToList();
  97. var firstLat = latitudes.First();
  98. var lastLat = latitudes.Last();
  99. var firstLong = longitudes.First();
  100. var lastLong = longitudes.Last();
  101. var resultLat = (firstLat + lastLat) / 2;
  102. var resultLong = (firstLong + lastLong) / 2;
  103. var firstLocation = new Location()
  104. { Latitude = firstLat, Longitude = firstLong };
  105. var lastLocation = new Location()
  106. { Latitude = lastLat, Longitude = lastLong };
  107. var distance = firstLocation.DistanceTo(lastLocation, UnitOfLength.Kilometers);
  108. coords = new Point(resultLat, resultLong);
  109. zoom = CalculateZoom(distance);
  110. }
  111. Coordinates = coords;
  112. ZoomLevel = zoom;
  113. }
  114. private static int CalculateZoom(double distance)
  115. {
  116. Dictionary<double, int> thresholds = new Dictionary<double, int>()
  117. {
  118. { 1, 17 },
  119. { 5, 16 },
  120. { 10, 15 },
  121. { 20, 11 },
  122. { 50, 10 },
  123. { 100, 9 },
  124. { 200, 8 },
  125. { 400, 7 },
  126. };
  127. foreach (var key in thresholds.Keys.OrderBy(x => x))
  128. {
  129. if (distance < key)
  130. return thresholds[key];
  131. }
  132. return 6;
  133. }
  134. private bool FilterJob(JobShell shell)
  135. {
  136. return _settings.SelectedCategories.Contains(CoreUtils.FullGuid)
  137. && (shell.Location.Latitude != 0.0F) && (shell.Location.Longitude != 0.0F)
  138. && (
  139. SearchText.IsNullOrWhiteSpace()
  140. || shell.JobNumber.Contains(SearchText, StringComparison.InvariantCultureIgnoreCase)
  141. || shell.Name.Contains(SearchText, StringComparison.InvariantCultureIgnoreCase));
  142. }
  143. private bool FilterEquipment(EquipmentShell shell)
  144. {
  145. return _settings.SelectedCategories.Contains(shell.GroupID)
  146. && (shell.Latitude != 0.0F) && (shell.Longitude != 0.0F)
  147. && (
  148. SearchText.IsNullOrWhiteSpace()
  149. || shell.Code.Contains(SearchText, StringComparison.InvariantCultureIgnoreCase)
  150. || shell.Description.Contains(SearchText, StringComparison.InvariantCultureIgnoreCase));
  151. }
  152. private async Task<bool> SelectFilter()
  153. {
  154. await Navigation.Popup<EquipmentMapsMenuViewModel>(model =>
  155. {
  156. model.SelectedItems = _settings.SelectedCategories.ToHashSet();
  157. model.OnChanged += (o, e) =>
  158. {
  159. _settings.SelectedCategories = model.SelectedItems.ToArray();
  160. new LocalConfiguration<EquipmentMapsSettings>().Save(_settings);
  161. Refresh();
  162. };
  163. });
  164. return true;
  165. }
  166. [RelayCommand]
  167. private void Search()
  168. {
  169. Refresh();
  170. }
  171. [RelayCommand]
  172. private void Reset()
  173. {
  174. CenterAndZoom(JobMarkers.Concat(EquipmentMarkers).Select(x => x.Coordinates).ToArray());
  175. }
  176. }