LiveMapsPanelViewModel.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Runtime.CompilerServices;
  8. using System.Windows;
  9. using System.Windows.Input;
  10. using System.Windows.Media.Imaging;
  11. using Comal.Classes;
  12. using ExCSS;
  13. using InABox.Clients;
  14. using InABox.Configuration;
  15. using InABox.Core;
  16. using InABox.WPF;
  17. using Syncfusion.UI.Xaml.Diagram;
  18. using Syncfusion.UI.Xaml.Maps;
  19. using GeoFence = Comal.Classes.GeoFence;
  20. using Point = System.Windows.Point;
  21. namespace PRSDesktop;
  22. public class MapMarker
  23. {
  24. public string Name { get; set; } = "Hi There";
  25. public string Label { get; set; } = "";
  26. public string Longitude { get; set; }
  27. public string Latitude { get; set; }
  28. }
  29. public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
  30. {
  31. private EquipmentGroup[]? _groups;
  32. private Equipment[]? _items;
  33. private EquipmentGroup? _selectedGroup;
  34. private string? _search;
  35. private DateTime _date = DateTime.Today;
  36. private Equipment? _selectedItem;
  37. private ObservableCollection<Point>? _waypoints;
  38. private Point _center;
  39. private double _radius;
  40. private GeoFence[]? _geofences;
  41. private ObservableCollection<MapElement> _elements;
  42. private ObservableCollection<MapMarker>? _sites;
  43. public EquipmentGroup[]? Groups
  44. {
  45. get => _groups;
  46. set => SetField(ref _groups, value);
  47. }
  48. public EquipmentGroup? SelectedGroup
  49. {
  50. get => _selectedGroup;
  51. set
  52. {
  53. SetField(ref _selectedGroup, value);
  54. SelectedItem = null;
  55. SaveSettings();
  56. OnPropertyChanged(nameof(Visible));
  57. }
  58. }
  59. public Equipment[]? Items
  60. {
  61. get => _items;
  62. set => SetField(ref _items, value);
  63. }
  64. public GeoFence[]? GeoFences
  65. {
  66. get => _geofences;
  67. set
  68. {
  69. _fencesMap.Clear();
  70. if (value is not null)
  71. {
  72. foreach (var fence in value ?? [])
  73. _fencesMap[fence] = Serialization.Deserialize<GeoFenceDefinition>(fence.Geofence) ?? new GeoFenceDefinition();
  74. }
  75. SetField(ref _geofences, value);
  76. }
  77. }
  78. private Dictionary<GeoFence,GeoFenceDefinition> _fencesMap = new();
  79. public string? Search
  80. {
  81. get => _search;
  82. set
  83. {
  84. SetField(ref _search, value);
  85. OnPropertyChanged(nameof(Visible));
  86. }
  87. }
  88. public Equipment[]? Visible => Items?.Where(x =>
  89. x.GroupLink.ID == (SelectedGroup?.ID ?? CoreUtils.FullGuid)
  90. && (
  91. string.IsNullOrEmpty(Search)
  92. || x.Code.Contains(Search,StringComparison.CurrentCultureIgnoreCase)
  93. || x.Description.Contains(Search,StringComparison.CurrentCultureIgnoreCase)
  94. )
  95. ).ToArray();
  96. public DateTime Date
  97. {
  98. get => _date;
  99. set
  100. {
  101. SetField(ref _date, value);
  102. ReloadLocations();
  103. OnPropertyChanged(nameof(Visible));
  104. }
  105. }
  106. public Equipment? SelectedItem
  107. {
  108. get => _selectedItem;
  109. set
  110. {
  111. SetField(ref _selectedItem, value);
  112. ReloadLocations();
  113. }
  114. }
  115. private void CenterMap()
  116. {
  117. var nwLon = double.MaxValue;
  118. var nwLat = double.MinValue;
  119. var seLon = double.MinValue;
  120. var seLat = double.MaxValue;
  121. var points = new List<Point>();
  122. if (_waypoints != null)
  123. points.AddRange(_waypoints);
  124. if (_geofences != null)
  125. {
  126. foreach (var geofence in _fencesMap.Where(x=>x.Key.Type == GPSLocationType.Office))
  127. points.AddRange(geofence.Value.Coordinates.Select(x => new Point(x.Latitude, x.Longitude)));
  128. }
  129. foreach (var point in points)
  130. {
  131. var lat = point.X;
  132. var lon = point.Y;
  133. if (lat != 0.0F && lon != 0.00)
  134. {
  135. nwLat = lat > nwLat ? lat : nwLat;
  136. seLat = lat < seLat ? lat : seLat;
  137. nwLon = lon < nwLon ? lon : nwLon;
  138. seLon = lon > seLon ? lon : seLon;
  139. }
  140. }
  141. var cLat = (nwLat + seLat) / 2.0F;
  142. var cLon = (nwLon + seLon) / 2.0F;
  143. //Center = new Point(-33.0 + new Random(DateTime.Now.Millisecond).NextDouble(), 115.0 + new Random(DateTime.Now.Millisecond).NextDouble());
  144. Center = new Point(cLat, cLon);
  145. var c = new Location { Latitude = cLat, Longitude = cLon };
  146. var nw = new Location { Latitude = nwLat, Longitude = nwLon };
  147. Radius = Math.Max(1.0, c.DistanceTo(nw, UnitOfLength.Kilometers) / 2.0F);
  148. }
  149. private void ReloadLocations()
  150. {
  151. var trackerids = Visible?.Select(x => x.TrackerLink.ID).ToArray() ?? [];
  152. Client.Query(
  153. new Filter<GPSTrackerLocation>(x=>x.Tracker.ID).InList(trackerids)
  154. .And(x => x.Location.Timestamp).IsGreaterThanOrEqualTo(Date.Date)
  155. .And(x => x.Location.Timestamp).IsLessThan(Date.Date.AddDays(1)),
  156. Columns.None<GPSTrackerLocation>()
  157. .Add(x=>x.Tracker.ID)
  158. .Add(x=>x.Location.Latitude)
  159. .Add(x=>x.Location.Longitude)
  160. .Add(x=>x.Location.Address)
  161. .Add(x=>x.Location.Timestamp),
  162. new SortOrder<GPSTrackerLocation>(x=>x.Location.Timestamp,SortDirection.Ascending),
  163. (data, _) =>
  164. {
  165. Dispatcher.BeginInvoke(() =>
  166. {
  167. Point[]? others = null;
  168. if (data is null)
  169. {
  170. EquipmentColorConverter.Pings = [];
  171. Waypoints = null;
  172. Sites = null;
  173. }
  174. else
  175. {
  176. EquipmentColorConverter.Pings = data.ToArray<GPSTrackerLocation>();
  177. others = data.Rows
  178. .Where(r => r.Get<GPSTrackerLocation,Guid>(c=>c.Tracker.ID) != (SelectedItem?.TrackerLink.ID ?? CoreUtils.FullGuid))
  179. .Select(r => new Point()
  180. {
  181. X = r.Get<GPSTrackerLocation, double>(c => c.Location.Latitude),
  182. Y = r.Get<GPSTrackerLocation, double>(c => c.Location.Longitude)
  183. }
  184. ).ToArray();
  185. var locations = data.Rows
  186. .Where(r => r.Get<GPSTrackerLocation,Guid>(c=>c.Tracker.ID) == (SelectedItem?.TrackerLink.ID ?? CoreUtils.FullGuid))
  187. .Select(r => new Point(
  188. r.Get<GPSTrackerLocation, double>(c => c.Location.Latitude),
  189. r.Get<GPSTrackerLocation, double>(c => c.Location.Longitude)
  190. )
  191. )?.ToArray() ?? [];
  192. Waypoints = new ObservableCollection<Point>(locations);
  193. Dictionary<GeoFence, MapMarker> sites = new Dictionary<GeoFence, MapMarker>();
  194. GeoFence? curFence = null;
  195. foreach (var row in data.Rows.Where(r => r.Get<GPSTrackerLocation,Guid>(c=>c.Tracker.ID) == (SelectedItem?.TrackerLink.ID ?? CoreUtils.FullGuid)))
  196. {
  197. var time = $"{row.Get<GPSTrackerLocation, DateTime>(c => c.Location.Timestamp):h:mm tt}";
  198. var geopoint = new GeoPoint(row.Get<GPSTrackerLocation, double>(c => c.Location.Latitude), row.Get<GPSTrackerLocation, double>(c => c.Location.Longitude));
  199. bool bFound = false;
  200. foreach (var geofence in _fencesMap)
  201. {
  202. if (geofence.Value.Contains(geopoint))
  203. {
  204. if (!sites.ContainsKey(geofence.Key))
  205. sites[geofence.Key] = new MapMarker()
  206. {
  207. Latitude = $"{geopoint.Latitude}",
  208. Longitude = $"{geopoint.Longitude}"
  209. };
  210. var timelist = sites[geofence.Key].Label.Split('\n').Where(x=>!string.IsNullOrWhiteSpace(x)).ToList();
  211. var first = timelist.FirstOrDefault() ?? "";
  212. if (!string.Equals(first,geofence.Key.Name))
  213. timelist.Insert(0,geofence.Key.Name);
  214. if ((geofence.Key == curFence) && timelist.Any())
  215. {
  216. var lasttime = timelist.Last().Split(" - ").ToList();
  217. timelist.Remove(timelist.Last());
  218. timelist.Add($"{lasttime.First()} - {time}");
  219. }
  220. else
  221. timelist.Add($"{time} - {time}");
  222. sites[geofence.Key].Label = string.Join("\n", timelist);
  223. curFence = geofence.Key;
  224. bFound = true;
  225. break;
  226. }
  227. }
  228. if (!bFound)
  229. curFence = null;
  230. }
  231. Sites = new ObservableCollection<MapMarker>(sites.Values);
  232. }
  233. OnPropertyChanged(nameof(Visible));
  234. RecalculateLayers(others);
  235. CenterMap();
  236. });
  237. }
  238. );
  239. }
  240. public ObservableCollection<MapElement> Elements
  241. {
  242. get => _elements;
  243. set => SetField(ref _elements, value);
  244. }
  245. private void RecalculateLayers(Point[]? markers)
  246. {
  247. var elements = new ObservableCollection<MapElement>();
  248. if (_geofences?.Any() == true)
  249. {
  250. foreach (var geofence in _geofences)
  251. {
  252. var definition = Serialization.Deserialize<GeoFenceDefinition>(geofence.Geofence) ?? new GeoFenceDefinition();
  253. var polygon = new MapPolygon()
  254. {
  255. Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.LightSalmon)
  256. { Opacity = 0.5 },
  257. Stroke = System.Windows.Media.Brushes.Firebrick,
  258. StrokeThickness = 0.75,
  259. Points = new ObservableCollection<Point>(
  260. definition.Coordinates.Select(x => new Point(x.Latitude, x.Longitude))),
  261. };
  262. elements.Add(polygon);
  263. }
  264. }
  265. if (markers?.Any() == true)
  266. {
  267. foreach (var marker in markers.Distinct())
  268. {
  269. var circle = new MapCircle()
  270. {
  271. Center = marker,
  272. Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Magenta) { Opacity = 0.5 },
  273. Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Black),
  274. StrokeThickness = 0,
  275. Radius = 10
  276. };
  277. elements.Add(circle);
  278. }
  279. }
  280. if (_waypoints?.Any( ) == true)
  281. {
  282. var line = new MapPolyline()
  283. {
  284. Points = new ObservableCollection<Point>(new ObservableCollection<Point>(_waypoints)),
  285. Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Navy) { Opacity = 0.5 },
  286. Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Navy) { Opacity = 0.5 },
  287. StrokeThickness = 4
  288. };
  289. elements.Add(line);
  290. }
  291. Elements = elements;
  292. }
  293. public ICommand RefreshCommand { get; set; }
  294. public ObservableCollection<MapMarker>? Sites
  295. {
  296. get => _sites;
  297. set => SetField(ref _sites, value);
  298. }
  299. public ObservableCollection<Point>? Waypoints
  300. {
  301. get => _waypoints;
  302. set => SetField(ref _waypoints, value);
  303. }
  304. public Point Center
  305. {
  306. get => _center;
  307. set => SetField(ref _center, value);
  308. }
  309. public double Radius
  310. {
  311. get => _radius;
  312. set => SetField(ref _radius, value);
  313. }
  314. public LiveMapsSettings Settings { get; private set; }
  315. public LiveMapsPanelViewModel()
  316. {
  317. Settings = new UserConfiguration<LiveMapsSettings>().Load();
  318. }
  319. public void SaveSettings()
  320. {
  321. Settings.GroupID = _selectedGroup?.ID ?? Guid.Empty;
  322. Settings.ItemID = _selectedItem?.ID ?? Guid.Empty;
  323. new UserConfiguration<LiveMapsSettings>().Save(Settings);
  324. }
  325. public void Refresh()
  326. {
  327. MultiQuery query = new MultiQuery();
  328. query.Add(new Filter<GeoFence>().All());
  329. query.Add(new Filter<EquipmentGroup>().All(),
  330. Columns.None<EquipmentGroup>()
  331. .Add(x => x.ID)
  332. .Add(x => x.Description)
  333. .Add(x=>x.Thumbnail.ID)
  334. );
  335. query.Add(
  336. new Filter<Document>(x=>x.ID).InQuery(new Filter<EquipmentGroup>().All(),x=>x.Thumbnail.ID));
  337. query.Add(
  338. new Filter<Equipment>(x => x.TrackerLink.ID).IsNotEqualTo(Guid.Empty),
  339. Columns.None<Equipment>()
  340. .Add(x => x.ID)
  341. .Add(x => x.Code)
  342. .Add(x => x.Description)
  343. .Add(x=>x.GroupLink.ID)
  344. .Add(x=>x.GroupLink.Thumbnail.ID)
  345. .Add(x=>x.TrackerLink.ID)
  346. .Add(x=>x.TrackerLink.Location.Timestamp)
  347. .Add(x=>x.TrackerLink.DeviceID)
  348. .Add(x=>x.TrackerLink.BatteryLevel)
  349. );
  350. query.Query( _ =>
  351. {
  352. Dispatcher.BeginInvoke(() =>
  353. {
  354. foreach (var row in query.Get<Document>().Rows)
  355. {
  356. var img = ImageUtils.LoadImage(row.Get<Document, byte[]>(x => x.Data));
  357. EquipmentThumbnailConverter.Cache[row.Get<Document, Guid>(x => x.ID)] = img;
  358. }
  359. // EquipmentThumbnailConverter.Cache = query.Get<Document>()
  360. // .ToDictionary<Document, Guid, BitmapImage?>(x => x.ID,
  361. // x => ImageUtils.LoadImage(x.Data));
  362. GeoFences = query.Get<GeoFence>().ToArray<GeoFence>();
  363. Groups = query.Get<EquipmentGroup>().ToArray<EquipmentGroup>();
  364. SelectedGroup = _groups?.FirstOrDefault(x=>x.ID == _selectedGroup?.ID);
  365. Items = query.Get<Equipment>().ToArray<Equipment>();
  366. SelectedItem = _items?.FirstOrDefault(x=>x.ID == _selectedItem?.ID);
  367. });
  368. });
  369. }
  370. public event PropertyChangedEventHandler? PropertyChanged;
  371. protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
  372. {
  373. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  374. }
  375. protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
  376. {
  377. if (EqualityComparer<T>.Default.Equals(field, value)) return false;
  378. field = value;
  379. OnPropertyChanged(propertyName);
  380. return true;
  381. }
  382. }