using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using Comal.Classes; using InABox.Clients; using InABox.Core; using Xamarin.Forms; using Xamarin.Forms.Xaml; using Syncfusion.SfMaps.XForms; using System.Threading; using System.IO; using Xamarin.Essentials; using Xamarin.CommunityToolkit.UI.Views; namespace comal.timesheets { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class LiveMapsTwo : ContentPage { Frame currentHeader = new Frame(); int currentPosition = 0; Equipment equipment = new Equipment(); Job job = new Job(); List types = new List(); string[] array; string country = "Australia"; bool bFirstLoad = true; double deviceHeight = 0.0F; Color On = Color.LightGreen; Color Off = Color.White; string saved = "SavedMapEquipment"; List frames = new List(); InABox.Core.Location CurrentJobLocation = new InABox.Core.Location(); InABox.Core.Location Perth = new InABox.Core.Location() { Latitude = -32.0410801, Longitude = 115.9957434 }; bool bButtonsTouched = false; Dictionary> EquipmentMappedToTypes = new Dictionary>(); List equipmentShellsToDisplay = new List(); List expanders = new List(); List equipmentShells = new List(); Dictionary images = new Dictionary(); Dictionary images1 = new Dictionary(); double DeviceWidth = 0.0F; public LiveMapsTwo() { InitializeComponent(); job = new Job(); equipment = new Equipment(); var mainDisplayInfo = DeviceDisplay.MainDisplayInfo; deviceHeight = mainDisplayInfo.Height; DeviceWidth = mainDisplayInfo.Width; sfMapLayer.GeoCoordinates = new Point(-32.0410801, 115.9957434); sfMap.ZoomLevel = 10; if (Device.RuntimePlatform.Equals(Device.iOS)) { //ImageSource imageSource = "mapmarker.png"; //sfMapLayer.MarkerSettings.MarkerIcon = MapMarkerIcon.Image; //sfMapLayer.MarkerSettings.ImageSource = "location-pin.pdf"; //sfMapLayer.MarkerSettings.IconSize = 35; sfMapLayer.MarkerSettings.MarkerIcon = MapMarkerIcon.Circle; sfMapLayer.MarkerSettings.IconColor = Color.DarkBlue; sfMapLayer.MarkerSettings.IconSize = 15; } else { sfMapLayer.MarkerSettings.MarkerIcon = MapMarkerIcon.Image; sfMapLayer.MarkerSettings.ImageSource = "mapmarker.png"; sfMapLayer.MarkerSettings.IconSize = 35; } sfMapLayer.MarkerSettings.FontAttributes = FontAttributes.Bold; sfMapLayer.MarkerSettings.LabelSize = 20; sfMapLayer.MarkerSettings.LabelColor = Color.DarkBlue; Timer t = new Timer(JobsBtnBounceCallBack, null, 0, 5000); LoadData(); } protected override void OnAppearing() { base.OnAppearing(); } private void Reset_Clicked(object sender, EventArgs e) { if (jobsBtn.BackgroundColor == On) { jobsBtn.BackgroundColor = Off; equipmentShellsToDisplay.Clear(); sfMapLayer.Markers.Clear(); flexLayout.Children.Clear(); Title = "Select Equipment"; foreach (Expander expander in expanders) { flexLayout.Children.Add(expander); } } sfMapLayer.Markers.Clear(); sfMapLayer.GeoCoordinates = new Point(-32.0410801, 115.9957434); sfMap.ZoomLevel = 10; equipmentShellsToDisplay.Clear(); } private async void JobsBtnBounceCallBack(object o) { if (jobsBtn.BackgroundColor == On) { await jobsBtn.TranslateTo(0, -15, 150); await jobsBtn.TranslateTo(0, 0, 150); await jobsBtn.TranslateTo(0, -7.5, 150); await jobsBtn.TranslateTo(0, 0, 150); } } private async void LoadData() { Title = "Live Maps - Loading"; await Task.Run(() => { if (Application.Current.Properties.ContainsKey(saved)) { equipmentShellsToDisplay = Application.Current.Properties[saved] as List; DisplayMarkersFromList(); } LoadGroups(); LoadEquipment(); LoadExpanders(); double width = (DeviceWidth / 10) * 4; Thread.Sleep(300); Device.BeginInvokeOnMainThread(async () => { if (Device.RuntimePlatform.Equals(Device.Android)) { Swipe.IsVisible = true; await Swipe.TranslateTo(width, 0, 1000); await Swipe.TranslateTo(0, 0, 1000); Swipe.IsVisible = false; } jobsBtn.RotateTo(360, 1000); }); }); } private void LoadGroups() { var client = new Client(); var table = client.Query( new Filter(x => x.Code).IsNotEqualTo(string.Empty), new Columns(x => x.Code, x => x.Thumbnail.ID), new SortOrder(x => x.Description) ); foreach (CoreRow row in table.Rows) { List list = row.Values; if (list[0] == null) { list[0] = ""; } //0 if (list[1] == null) { list[1] = Guid.Empty; } //1 EquipmentGroupShell group = new EquipmentGroupShell() { Code = list[0].ToString(), ThumbnailID = Guid.Parse(list[1].ToString()) }; //TODO remove Comal-specific group filtering if (group.Code != "ZZINACTIVE" && group.Code != "CALIBRATED" && group.Code != "DEVICE" && group.Code != "FACTORY") { types.Add(group.Code); CoreTable table2 = new Client().Query ( new Filter(x => x.ID).IsEqualTo(group.ThumbnailID), new Columns(x => x.Data) ); if (table2.Rows.Any()) { CoreRow docrow = table2.Rows.FirstOrDefault(); if (docrow != null) { byte[] data = docrow.Get(x => x.Data); ImageSource src = ImageSource.FromStream(() => new MemoryStream(data)); Image img = new Image(); img.HeightRequest = 30; img.WidthRequest = 30; img.Aspect = Aspect.AspectFit; img.Source = src; images.Add(group.Code, img); Image img1 = new Image(); img1.HeightRequest = 30; img1.WidthRequest = 30; img1.Aspect = Aspect.AspectFit; img1.Source = src; images1.Add(group.Code, img1); } } } } } private void LoadEquipment() { Filter filter = new Filter(x => x.GroupLink.Code).IsEqualTo(types.First()); foreach (string s in types) { if (s != types.First()) filter = filter.Or(x => x.GroupLink.Code).IsEqualTo(s); } CoreTable table = new Client().Query( filter, new Columns( x => x.GroupLink.Code, //0 x => x.Description, //1 x => x.TrackerLink.Location.Latitude, //2 x => x.TrackerLink.Location.Longitude, //3 x => x.LastUpdate //4 ) ); foreach (CoreRow row in table.Rows) { List list = row.Values; if (list[0] == null) { list[0] = ""; } //0 if (list[1] == null) { list[1] = ""; } //1 if (list[2] == null) { list[2] = 0.0F; } //2 if (list[3] == null) { list[3] = 0.0F; } //3 if (list[4] == null) { list[4] = DateTime.MinValue; }//4 EquipmentShell equipmentShell = new EquipmentShell() { Code = list[0].ToString(), Description = list[1].ToString(), Latitude = double.Parse(list[2].ToString()), Longitude = double.Parse(list[3].ToString()), LastUpdate = DateTime.Parse(list[4].ToString()), }; equipmentShell.Location = new InABox.Core.Location() { Latitude = equipmentShell.Latitude, Longitude = equipmentShell.Longitude }; if (equipmentShell.Longitude == 0.0F && equipmentShell.Latitude == 0.0F) { //do nothing } else equipmentShells.Add(equipmentShell); } } private void LoadExpanders() { List equipmentShellsToDisplayNames = new List(); if (equipmentShellsToDisplay.Count > 0) { foreach (EquipmentShell shell in equipmentShellsToDisplay) { equipmentShellsToDisplayNames.Add(shell.Description); } } Dictionary> ListsDictionary = new Dictionary>(); foreach (string s in types) { List shells = new List(); List optionStrings = new List(); foreach (EquipmentShell equipmentShell in equipmentShells) { if (equipmentShell.Code == s) { OptionString optionString = new OptionString() { Value = equipmentShell.Description }; if (equipmentShellsToDisplayNames.Contains(equipmentShell.Description)) { optionString.Color = On; } optionStrings.Add(optionString); shells.Add(equipmentShell); } } if (optionStrings.Count > 16) { optionStrings.Add(new OptionString { Value = "" }); optionStrings.Add(new OptionString { Value = "" }); optionStrings.Add(new OptionString { Value = "" }); } EquipmentMappedToTypes.Add(s, shells); ListsDictionary.Add(s, optionStrings); Label label = new Label { Text = s, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center, VerticalTextAlignment = TextAlignment.Center }; label.FontSize = 18; Grid grid = new Grid(); grid.Margin = 0; grid.Padding = 0; grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); Frame frame = new Frame(); frame.BackgroundColor = Color.White; frame.HasShadow = false; frame.CornerRadius = 15; frame.BorderColor = Color.Transparent; frame.Padding = new Thickness(12, 2, 12, 2); frame.Margin = new Thickness(0, 0, 0, 5); frame.HorizontalOptions = LayoutOptions.Center; Image image = new Image(); try { image = images[s]; } catch { } if (image != null) { Grid.SetColumn(image, 0); Grid.SetColumn(label, 1); image.Margin = 0; grid.Children.Add(image); grid.Children.Add(label); frame.Content = grid; } else { frame.Content = label; } Expander expander = new Expander { Header = frame }; DataTemplate template = new DataTemplate(() => { Button valueBtn = new Button(); valueBtn.SetBinding(Button.TextProperty, "Value"); valueBtn.SetBinding(BackgroundColorProperty, "Color"); valueBtn.Clicked += ValueBtn_Clicked; valueBtn.TextColor = Color.DarkGray; valueBtn.CornerRadius = 15; valueBtn.BorderColor = Color.Transparent; valueBtn.Margin = new Thickness(10, 1, 10, 1); return new ViewCell { View = valueBtn }; }); ListView listView = new ListView(); listView.ItemTemplate = template; listView.ItemsSource = ListsDictionary[s]; expander.Content = listView; expander.ExpandAnimationLength = 100; expander.ExpandAnimationEasing = Easing.CubicOut; expander.CollapseAnimationLength = 0; expander.Margin = new Thickness(2, 2, 2, 5); expander.BackgroundColor = Color.Transparent; expander.Tapped += Expander_Tapped; expanders.Add(expander); Device.BeginInvokeOnMainThread(() => { flexLayout.Children.Add(expander); }); GenerateFrame(s); } bFirstLoad = false; Device.BeginInvokeOnMainThread(() => { Title = "Select Equipment"; jobsBtn.IsEnabled = true; }); } private void GenerateFrame(string s) { Label label1 = new Label { Text = s, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center, VerticalTextAlignment = TextAlignment.Center }; label1.FontSize = 18; Grid grid1 = new Grid(); grid1.Margin = 0; grid1.Padding = 0; grid1.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); grid1.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); Image image1 = new Image(); try { image1 = images1[s]; } catch { } Frame frame1 = new Frame(); frame1.BackgroundColor = Off; frame1.HasShadow = false; frame1.CornerRadius = 15; frame1.BorderColor = Color.Transparent; frame1.Padding = new Thickness(15, 2, 15, 2); frame1.Margin = new Thickness(5, 0, 5, 5); frame1.HorizontalOptions = LayoutOptions.Center; if (image1 != null) { Grid.SetColumn(image1, 0); Grid.SetColumn(label1, 1); image1.Margin = 0; grid1.Children.Add(image1); grid1.Children.Add(label1); frame1.Content = grid1; } else { frame1.Content = label1; } TapGestureRecognizer tapGestureRecognizer = new TapGestureRecognizer() { NumberOfTapsRequired = 1 }; tapGestureRecognizer.Tapped += ((object sender, EventArgs e) => { bButtonsTouched = true; Frame thisFrame = sender as Frame; if (thisFrame.BackgroundColor == Off) { sfMapLayer.Markers.Clear(); thisFrame.BackgroundColor = On; image1.BackgroundColor = On; foreach (EquipmentShell equipment in EquipmentMappedToTypes[s]) { if (CurrentJobLocation.DistanceTo(equipment.Location, UnitOfLength.Kilometers) < 1.5) { equipmentShellsToDisplay.Add(equipment); } } DisplayMarkersFromList(); } else if (thisFrame.BackgroundColor == On) { sfMapLayer.Markers.Clear(); thisFrame.BackgroundColor = Off; foreach (EquipmentShell equipment in EquipmentMappedToTypes[s]) { if (CurrentJobLocation.DistanceTo(equipment.Location, UnitOfLength.Kilometers) < 1.5) { equipmentShellsToDisplay.Remove(equipment); } } DisplayMarkersFromList(); } }); frame1.GestureRecognizers.Add(tapGestureRecognizer); frames.Add(frame1); } private void ValueBtn_Clicked(object sender, EventArgs e) { Button valueBtn = sender as Button; if (valueBtn.BackgroundColor == Off) { valueBtn.BackgroundColor = On; equipmentShellsToDisplay.Add(equipmentShells.Find(x => x.Description.Equals(valueBtn.Text))); } else { valueBtn.BackgroundColor = Off; equipmentShellsToDisplay.Remove(equipmentShellsToDisplay.Find(x => x.Description.Equals(valueBtn.Text))); } } protected override void OnDisappearing() { if (equipmentShellsToDisplay.Count > 0) { if (!Application.Current.Properties.ContainsKey(saved)) { Application.Current.Properties.Add(saved, equipmentShellsToDisplay); } else { Application.Current.Properties.Remove(saved); Application.Current.Properties.Add(saved, equipmentShellsToDisplay); } } else { Application.Current.Properties.Remove(saved); } base.OnDisappearing(); } private void Expander_Tapped(object sender, EventArgs e) { Expander exp = sender as Expander; if (exp.State.Equals(ExpandState.Expanding) || exp.State.Equals(ExpandState.Expanded)) { currentHeader = exp.Header as Frame; foreach (Expander expander in expanders) { if (!expander.Equals(sender)) { flexLayout.Children.Remove(expander); } else { currentPosition = expanders.IndexOf(expander); exp.Header = CreateFrame(); } } scrollView.HeightRequest = deviceHeight; scrollView.ScrollToAsync(exp, ScrollToPosition.MakeVisible, false); } else if (exp.State.Equals(ExpandState.Collapsing) || exp.State.Equals(ExpandState.Collapsed)) { scrollView.HeightRequest = 45; scrollView.ScrollToAsync(exp, ScrollToPosition.MakeVisible, false); flexLayout.Children.Clear(); exp.Header = currentHeader; foreach (Expander expander in expanders) { flexLayout.Children.Add(expander); } sfMapLayer.Markers.Clear(); DisplayMarkersFromList(); } } private Frame CreateFrame() { Label label = new Label { Text = "Tap Here to View or Cancel", FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center, VerticalTextAlignment = TextAlignment.Center, LineBreakMode = LineBreakMode.WordWrap }; label.FontSize = Device.GetNamedSize(NamedSize.Large, label); Grid grid = new Grid(); grid.Margin = 0; grid.Padding = 0; grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); Frame frame = new Frame(); frame.BackgroundColor = Color.Orange; frame.HasShadow = false; frame.CornerRadius = 15; frame.BorderColor = Color.Transparent; frame.Padding = new Thickness(10); frame.Margin = new Thickness(5); frame.HorizontalOptions = LayoutOptions.Center; frame.HeightRequest = 40; Image image = new Image(); image.Source = "mapicon.png"; Grid.SetColumn(image, 0); Grid.SetColumn(label, 1); image.Margin = 0; grid.Children.Add(image); grid.Children.Add(label); frame.Content = grid; return frame; } private async void DisplayMarkersFromList() { await Task.Run(() => { CalculateCenter(); foreach (EquipmentShell equipment in equipmentShellsToDisplay) { MapMarker marker = new MapMarker(); marker.Label = equipment.Description; marker.Latitude = equipment.Latitude.ToString(); marker.Longitude = equipment.Longitude.ToString(); Device.BeginInvokeOnMainThread(() => { sfMapLayer.Markers.Add(marker); }); } }); } private async void CalculateCenter() { if (equipmentShellsToDisplay.Count == 1) { EquipmentShell equipment = equipmentShellsToDisplay.First(); Device.BeginInvokeOnMainThread(() => { sfMapLayer.GeoCoordinates = new Point(equipment.Latitude, equipment.Longitude); sfMap.ZoomLevel = 15; }); } else if (equipmentShellsToDisplay.Count > 1) { List latitudes = new List(); List longitudes = new List(); foreach (EquipmentShell equipment in equipmentShellsToDisplay) { latitudes.Add(equipment.Latitude); longitudes.Add(equipment.Longitude); } latitudes.Sort(); longitudes.Sort(); double firstLat = latitudes.First(); double lastLat = latitudes.Last(); double firstLong = longitudes.First(); double lastLong = longitudes.Last(); double resultLat = (firstLat + lastLat) / 2; double resultLong = (firstLong + lastLong) / 2; InABox.Core.Location firstLocation = new InABox.Core.Location() { Latitude = firstLat, Longitude = firstLong }; InABox.Core.Location lastLocation = new InABox.Core.Location() { Latitude = lastLat, Longitude = lastLong }; double distance = firstLocation.DistanceTo(lastLocation, UnitOfLength.Kilometers); CalculateZoom(distance); Device.BeginInvokeOnMainThread(() => { sfMapLayer.GeoCoordinates = new Point(resultLat, resultLong); }); } } private void CalculateZoom(double distance) { Device.BeginInvokeOnMainThread(() => { if (distance < 1) { sfMap.ZoomLevel = 17; } else if (distance >= 1 && distance <= 5) { sfMap.ZoomLevel = 16; } else if (distance >= 5 && distance <= 10) { sfMap.ZoomLevel = 15; } else if (distance >= 10 && distance <= 20) { sfMap.ZoomLevel = 11; } else if (distance >= 20 && distance <= 50) { sfMap.ZoomLevel = 10; } else if (distance >= 50 && distance <= 100) { sfMap.ZoomLevel = 9; } else if (distance >= 100 && distance <= 200) { sfMap.ZoomLevel = 8; } else if (distance >= 200 && distance <= 400) { sfMap.ZoomLevel = 7; } else if (distance >= 400) { sfMap.ZoomLevel = 6; } }); } private async void JobsBtn_Clicked(object sender, EventArgs e) { if (jobsBtn.BackgroundColor == On) { jobsBtn.BackgroundColor = Off; equipmentShellsToDisplay.Clear(); sfMapLayer.Markers.Clear(); flexLayout.Children.Clear(); Title = "Select Equipment"; foreach (Expander expander in expanders) { flexLayout.Children.Add(expander); } } else { var jobs = GlobalVariables.JobShells.Where(x => x.JobStatusDescription.Equals("Active")); List names = new List(); foreach (var jobshell in jobs) { string s = jobshell.Name; if (!string.IsNullOrWhiteSpace(s) && s != "Empty Job") names.Add(s); } string[] actions = names.ToArray(); string result = await DisplayActionSheet("Select Job", "Cancel", null, actions); if (string.IsNullOrEmpty(result) || result.Equals("Cancel")) return; Device.BeginInvokeOnMainThread(() => { Title = "Loading Job"; }); try { await Task.Run(async () => { CustomControls.JobShell _jobShell = GlobalVariables.JobShells.Find(x => x.Name.Equals(result)); Job job = new Client().Query(new Filter(x => x.ID).IsEqualTo(_jobShell.ID)).Rows.First().ToObject(); var xamlocation = (await Geocoding.GetLocationsAsync($"{job.SiteAddress.Street}, {job.SiteAddress.City}, {"Australia"}")).FirstOrDefault(); if (xamlocation == null) return; CurrentJobLocation = new InABox.Core.Location() { Latitude = xamlocation.Latitude, Longitude = xamlocation.Longitude }; equipmentShellsToDisplay.Clear(); if (CurrentJobLocation.DistanceTo(Perth, UnitOfLength.Kilometers) > 500) { DisplayAlert("Error with Address", "Address on file: " + job.SiteAddress + " " + job.SiteAddress.City, "OK"); } else { Device.BeginInvokeOnMainThread(() => { Title = "Equipment by Job"; sfMapLayer.Markers.Clear(); sfMap.ZoomLevel = 18; sfMapLayer.GeoCoordinates = new Point(xamlocation.Latitude, xamlocation.Longitude); Title = result; flexLayout.Children.Clear(); flexLayout.HeightRequest = 55; jobsBtn.BackgroundColor = On; foreach (Frame frame in frames) { flexLayout.Children.Add(frame); } }); if (Device.RuntimePlatform.Equals(Device.Android)) { bButtonsTouched = false; Thread.Sleep(2000); HighlightFrames(); Timer t = new Timer(ButtonsNotTouched, null, 0, 6000); } } }); } catch { DisplayAlert("Error with Address", "Address on file: " + job.SiteAddress + " " + job.SiteAddress.City, "OK"); Device.BeginInvokeOnMainThread(() => { Title = "Select Equipment"; }); } } } private async void HighlightFrames() { foreach (Frame frame in frames) { frame.BackgroundColor = Color.Yellow; Thread.Sleep(750); frame.BackgroundColor = Off; } } private void ButtonsNotTouched(object o) { if (!bButtonsTouched) { HighlightFrames(); } } private async void ShowJobLocation(string street, string city, string name) { try { var location = (await Geocoding.GetLocationsAsync($"{street}, {city}, {country}")).FirstOrDefault(); if (location == null) return; sfMap.ZoomLevel = 15; sfMapLayer.GeoCoordinates = new Point(location.Latitude, location.Longitude); MapMarker marker1 = new MapMarker(); marker1.Label = name; marker1.Latitude = location.Latitude.ToString(); marker1.Longitude = location.Longitude.ToString(); sfMapLayer.Markers.Add(marker1); sfMapLayer.MarkerSettings.IconSize = 15; sfMapLayer.MarkerSettings.FontAttributes = FontAttributes.Bold; sfMapLayer.MarkerSettings.LabelSize = 20; sfMapLayer.MarkerSettings.LabelColor = Color.DarkRed; sfMapLayer.MarkerSettings.IconColor = Color.Red; if (!string.IsNullOrWhiteSpace(name)) { Title = name; } equipment = null; } catch (Exception e) { await DisplayAlert("Error Loading Address.", street + " " + city + " Not found.", "OK"); return; } } } public class OptionString { public string Value { get; set; } public Color Color { get; set; } public OptionString() { Value = ""; Color = Color.White; } } public class EquipmentShell { public string Code { get; set; } public string Description { get; set; } public double Latitude { get; set; } public double Longitude { get; set; } public DateTime LastUpdate { get; set; } public InABox.Core.Location Location { get; set; } public EquipmentShell() { Code = ""; Description = ""; Latitude = 0.0F; Longitude = 0.0F; LastUpdate = DateTime.MinValue; Location = new InABox.Core.Location(); } } public class EquipmentGroupShell { public String Code { get; set; } public Guid ThumbnailID { get; set; } public EquipmentGroupShell() { Code = ""; ThumbnailID = Guid.Empty; } } }