using System; using System.Collections.ObjectModel; using System.Linq; using System.Net.Http; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; using Geocoding; using Geocoding.Google; using InABox.Clients; using InABox.Core; using InABox.WPF; using Nominatim.API.Geocoders; using Nominatim.API.Models; using Nominatim.API.Web; using Syncfusion.UI.Xaml.Maps; using Address = InABox.Core.Address; using OnAfterLoad = InABox.DynamicGrid.OnAfterLoad; using Point = System.Windows.Point; namespace InABox.Wpf.DynamicGrid; public partial class GeofenceEditor : Window { public Address Address { get; private set; } GeoFenceDefinition _definition = null; //private ImageryLayer ImageryLayer; //private MapPolygon Polygon; //private SubShapeFileLayer ShapeLayer; private bool _canEdit = false; public GeofenceEditor(Address address, bool canEdit) { _canEdit = canEdit; Address = address; _definition = Serialization.Deserialize(address.Geofence) ?? new GeoFenceDefinition(); InitializeComponent(); // ImageryLayer = string.IsNullOrWhiteSpace(CoreUtils.GoogleAPIKey) // ? new ImageryLayer() // : new GoogleImageryLayer() { Type = GoogleImageryLayerType.Satellite }; // ImageryLayer.Radius = 10; // // Map.Layers.Add(ImageryLayer); // // Polygon = new MapPolygon() // { // Fill = new SolidColorBrush(Colors.LightSalmon) { Opacity = 0.5 }, // Stroke = new SolidColorBrush(Colors.Firebrick), // StrokeThickness = 0.75, // Points = new CoreObservableCollection() // }; // ShapeLayer = new SubShapeFileLayer() { MapElements = new ObservableCollection([Polygon]) }; // // ImageryLayer.SubShapeFileLayers = new ObservableCollection([ShapeLayer]); SetGeometry.Visibility = canEdit ? Visibility.Visible : Visibility.Collapsed; SetRadius.Visibility = canEdit ? Visibility.Visible : Visibility.Collapsed; SearchBar.Visibility = canEdit ? Visibility.Visible : Visibility.Collapsed; Street.Text = address.Street; City.Text = address.City; State.Text = address.State; PostCode.Text = Address.PostCode; Title = $"Geofence Editor ({(string.IsNullOrWhiteSpace(CoreUtils.GoogleAPIKey) ? "OSM" : "Google")})"; } public override void OnApplyTemplate() { base.OnApplyTemplate(); if (_canEdit && string.IsNullOrWhiteSpace(Address.Geofence)) Task.Run(CheckAddress); else SetupMap(); } private class HttpClientFactory : IHttpClientFactory { private static HttpClient? _client; public HttpClient CreateClient(string name) { _client ??= new HttpClient(); return _client; } } private static HttpClientFactory? _httpClientFactory = null; private async Task CheckAddress() { if (!string.IsNullOrWhiteSpace(CoreUtils.GoogleAPIKey)) { IGeocoder geocoder = new GoogleGeocoder(CoreUtils.GoogleAPIKey); try { var matches = await geocoder.GeocodeAsync( $"{Address.Street}, {Address.City} {Address.State} {Address.PostCode} Australia"); var match = matches.FirstOrDefault(); if (match != null) { Address.Location.Longitude = match.Coordinates.Longitude; Address.Location.Latitude = match.Coordinates.Latitude; if (match is GoogleAddress gAdd) { _definition.Coordinates.Clear(); _definition.Coordinates.Add(new GeoPoint(gAdd.Bounds.NorthEast.Latitude, gAdd.Bounds.NorthEast.Longitude)); _definition.Coordinates.Add(new GeoPoint(gAdd.Bounds.NorthEast.Latitude, gAdd.Bounds.SouthWest.Longitude)); _definition.Coordinates.Add(new GeoPoint(gAdd.Bounds.SouthWest.Latitude, gAdd.Bounds.SouthWest.Longitude)); _definition.Coordinates.Add(new GeoPoint(gAdd.Bounds.SouthWest.Latitude, gAdd.Bounds.NorthEast.Longitude)); _definition.Coordinates.Add(new GeoPoint(gAdd.Bounds.NorthEast.Latitude, gAdd.Bounds.NorthEast.Longitude)); } else SquareFence(20.0); Address.Geofence = Serialization.Serialize(_definition); } else { Address.Location.Longitude = 0.0; Address.Location.Latitude = 0.0; Address.Geofence = string.Empty; } } catch (Exception e) { Logger.Send(LogType.Error, ClientFactory.UserID, $"{e.Message}\n{e.StackTrace}"); } } else { _httpClientFactory ??= new HttpClientFactory(); NominatimWebInterface intf = new NominatimWebInterface(_httpClientFactory); var searcher = new ForwardGeocoder(intf); var request = new ForwardGeocodeRequest() { StreetAddress = Address.Street, City = Address.City, // County="", State = Address.State, PostalCode = Address.PostCode, Country = "AU", //queryString = $"{Address.Street}, {Address.City}, {Address.State}, {Address.PostCode} Australia", //BreakdownAddressElements = true, ShowExtraTags = true, ShowAlternativeNames = true, ShowGeoJSON = true, }; try { var matches = await searcher.Geocode(request); var match = matches.FirstOrDefault(); if (match != null) { Address.Location.Longitude = match.Longitude; Address.Location.Latitude = match.Latitude; if (match.BoundingBox.HasValue) { var bbox = match.BoundingBox.Value; _definition.Coordinates.Clear(); _definition.Coordinates.Add(new GeoPoint(bbox.minLatitude, bbox.minLongitude)); _definition.Coordinates.Add(new GeoPoint(bbox.minLatitude, bbox.maxLongitude)); _definition.Coordinates.Add(new GeoPoint(bbox.maxLatitude, bbox.maxLongitude)); _definition.Coordinates.Add(new GeoPoint(bbox.maxLatitude, bbox.minLongitude)); _definition.Coordinates.Add(new GeoPoint(bbox.minLatitude, bbox.minLongitude)); } else SquareFence(20.0); Address.Geofence = Serialization.Serialize(_definition); } else { Address.Location.Longitude = 0.0; Address.Location.Latitude = 0.0; Address.Geofence = string.Empty; } } catch (Exception e) { Logger.Send(LogType.Error, ClientFactory.UserID, $"{e.Message}\n{e.StackTrace}"); } } Dispatcher.BeginInvoke(SetupMap); } private void SquareFence(double side) { _definition.Coordinates.Clear(); _definition.Coordinates.Add(new GeoPoint(Address.Location.Latitude, Address.Location.Longitude).Move(0-(side/2.0),0-(side/2.0))); _definition.Coordinates.Add(new GeoPoint(Address.Location.Latitude, Address.Location.Longitude).Move(0-(side/2.0),side/2.0)); _definition.Coordinates.Add(new GeoPoint(Address.Location.Latitude, Address.Location.Longitude).Move(side/2.0,side/2.0)); _definition.Coordinates.Add(new GeoPoint(Address.Location.Latitude, Address.Location.Longitude).Move(side/2.0,0-(side/2.0))); _definition.Coordinates.Add(new GeoPoint(Address.Location.Latitude, Address.Location.Longitude).Move(0-(side/2.0),0-(side/2.0))); } private void SetupMap() { CenterMap(); RadiusSlider.ValueChanged -= RadiusSliderChanged; RadiusSlider.Value = 20.0; if (_definition.Coordinates.Count < 4) SquareFence(20.0); RadiusSlider.ValueChanged += RadiusSliderChanged; UpdateMap(); } private void CenterMap() { ImageryLayer.Center = new Point(Address.Location.Latitude, Address.Location.Longitude); ImageryLayer.Markers = new CoreObservableCollection([new MapMarker() { Latitude = $"{Address.Location.Latitude:F15}", Longitude = $"{Address.Location.Longitude:F15}" }]); } private void RadiusSliderChanged(object sender, RoutedPropertyChangedEventArgs e) { RecalculateSquareFence(); UpdateMap(); } private void RecalculateSquareFence() { SquareFence(RadiusSlider.Value); Address.Geofence = Serialization.Serialize(_definition); } private void UpdateMap() { // if (Polygon.Points is CoreObservableCollection _pts) // _pts.ReplaceRange(_definition.Coordinates.Select(p => new Point(p.Latitude, p.Longitude))); Polygon.Points.Clear(); foreach (var p in _definition.Coordinates) Polygon.Points.Add(new Point(p.Latitude, p.Longitude)); ShapeLayer.Refresh(); } private void ZoomIn_OnClick(object sender, RoutedEventArgs e) { Map.MaxZoom = Math.Min(20, Map.ZoomLevel + 1); Map.ZoomLevel = Math.Min(20, Map.ZoomLevel + 1); UpdateMap(); } private void ZoomOut_OnClick(object sender, RoutedEventArgs e) { Map.MinZoom= Math.Max(5, Map.ZoomLevel - 1); Map.ZoomLevel = Math.Max(5, Map.ZoomLevel - 1); UpdateMap(); } private enum GeoFenceAction { None, Polygon, Square, Coordinates } private GeoFenceAction _action = GeoFenceAction.None; private void UpdateButtons(GeoFenceAction action) { _action = action; CloseEditor.Visibility = action == GeoFenceAction.None ? Visibility.Collapsed : Visibility.Visible; SetGeometry.Visibility = action == GeoFenceAction.None || action == GeoFenceAction.Polygon ? Visibility.Visible : Visibility.Collapsed; SetGeometry.IsEnabled = action == GeoFenceAction.None; SetGeometry.Background = action == GeoFenceAction.Polygon ? Brushes.Yellow : Brushes.Silver; SetRadius.Visibility = action == GeoFenceAction.None || action == GeoFenceAction.Square ? Visibility.Visible : Visibility.Collapsed; SetRadius.IsEnabled = action == GeoFenceAction.None; SetRadius.Background = action == GeoFenceAction.Square ? Brushes.Yellow : Brushes.Silver; RadiusSlider.Visibility = action == GeoFenceAction.Square ? Visibility.Visible : Visibility.Collapsed; SetCoordinates.Visibility = action == GeoFenceAction.None || action == GeoFenceAction.Coordinates ? Visibility.Visible : Visibility.Collapsed; SetCoordinates.IsEnabled = action == GeoFenceAction.None; SetCoordinates.Background = action == GeoFenceAction.Coordinates ? Brushes.Yellow : Brushes.Silver; } private void SetGeometry_OnClick(object sender, RoutedEventArgs e) { //if (_action == GeoFenceAction.Polygon) // UpdateButtons(GeoFenceAction.None); //else //{ UpdateButtons(GeoFenceAction.Polygon); _definition.Coordinates.Clear(); UpdateMap(); //} } private void SetRadius_OnClick(object sender, RoutedEventArgs e) { //if (_action == GeoFenceAction.Square) // UpdateButtons(GeoFenceAction.None); //else //{ UpdateButtons(GeoFenceAction.Square); RadiusSlider.ValueChanged -= RadiusSliderChanged; RadiusSlider.Value = 20.0; SquareFence(20.0); RadiusSlider.ValueChanged += RadiusSliderChanged; RecalculateSquareFence(); UpdateMap(); //} } private void SearchAddress_Click(object sender, RoutedEventArgs e) { Task.Run(CheckAddress); } private void SetCoordinates_OnClick(object sender, RoutedEventArgs e) { //if (_action == GeoFenceAction.Coordinates) // UpdateButtons(GeoFenceAction.None); //else { UpdateButtons(GeoFenceAction.Coordinates); _definition.Coordinates.Clear(); UpdateMap(); } } private void CloseEditor_OnClick(object sender, RoutedEventArgs e) { UpdateButtons(GeoFenceAction.None); } private void Map_OnPreviewMouseUp(object sender, MouseButtonEventArgs e) { var point = Mouse.GetPosition(Map); System.Console.WriteLine($@"Preview: {point.X}, {point.Y}"); Dispatcher.BeginInvoke(() => { var latlon = ImageryLayer.GetLatLonFromPoint(point); var geopoint = new GeoPoint(latlon.Y, latlon.X); if (_action == GeoFenceAction.Polygon) { if (!_definition.Coordinates.Any()) _definition.Coordinates.Add(geopoint.Copy()); _definition.Coordinates.Insert(_definition.Coordinates.Count - 1, geopoint.Copy()); Address.Geofence = Serialization.Serialize(_definition); UpdateMap(); e.Handled = true; } else if (_action == GeoFenceAction.Coordinates) { Address.Location.Latitude = geopoint.Latitude; Address.Location.Longitude = geopoint.Longitude; CenterMap(); UpdateMap(); e.Handled = true; } }); } }