|
@@ -1,32 +1,32 @@
|
|
using System;
|
|
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using System.Linq;
|
|
|
|
+using System.Net.Http;
|
|
using System.Threading.Tasks;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows;
|
|
using System.Windows.Input;
|
|
using System.Windows.Input;
|
|
|
|
+using System.Windows.Media;
|
|
using Geocoding;
|
|
using Geocoding;
|
|
using Geocoding.Google;
|
|
using Geocoding.Google;
|
|
|
|
+using InABox.Clients;
|
|
using InABox.Core;
|
|
using InABox.Core;
|
|
|
|
+using Nominatim.API.Geocoders;
|
|
|
|
+using Nominatim.API.Models;
|
|
|
|
+using Nominatim.API.Web;
|
|
using Syncfusion.UI.Xaml.Maps;
|
|
using Syncfusion.UI.Xaml.Maps;
|
|
using Address = InABox.Core.Address;
|
|
using Address = InABox.Core.Address;
|
|
using Point = System.Windows.Point;
|
|
using Point = System.Windows.Point;
|
|
|
|
|
|
namespace InABox.Wpf.DynamicGrid;
|
|
namespace InABox.Wpf.DynamicGrid;
|
|
|
|
|
|
-public class GoogleImageryLayer : ImageryLayer
|
|
|
|
-{
|
|
|
|
- protected override string GetUri(int X, int Y, int Scale)
|
|
|
|
- {
|
|
|
|
- var link = $"https://mt1.google.com/vt/lyrs=m@221097413,3&x={X}&y={Y}&z={Scale}";
|
|
|
|
- return link;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
public partial class GeofenceEditor : Window
|
|
public partial class GeofenceEditor : Window
|
|
{
|
|
{
|
|
public Address Address { get; private set; }
|
|
public Address Address { get; private set; }
|
|
|
|
|
|
GeoFenceDefinition _definition = null;
|
|
GeoFenceDefinition _definition = null;
|
|
|
|
+
|
|
|
|
+ private ImageryLayer ImageryLayer;
|
|
|
|
+ public MapPolygon Polygon;
|
|
|
|
|
|
public GeofenceEditor(Address address, bool canEdit)
|
|
public GeofenceEditor(Address address, bool canEdit)
|
|
{
|
|
{
|
|
@@ -34,6 +34,22 @@ public partial class GeofenceEditor : Window
|
|
_definition = Serialization.Deserialize<GeoFenceDefinition>(address.Geofence) ?? new GeoFenceDefinition();
|
|
_definition = Serialization.Deserialize<GeoFenceDefinition>(address.Geofence) ?? new GeoFenceDefinition();
|
|
|
|
|
|
InitializeComponent();
|
|
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,
|
|
|
|
+ };
|
|
|
|
+ var layer = new SubShapeFileLayer() { MapElements = new ObservableCollection<MapElement>([Polygon]) };
|
|
|
|
+ ImageryLayer.SubShapeFileLayers = new ObservableCollection<SubShapeFileLayer>([layer]);
|
|
|
|
|
|
SetGeometry.Visibility = canEdit ? Visibility.Visible : Visibility.Collapsed;
|
|
SetGeometry.Visibility = canEdit ? Visibility.Visible : Visibility.Collapsed;
|
|
SetRadius.Visibility = canEdit ? Visibility.Visible : Visibility.Collapsed;
|
|
SetRadius.Visibility = canEdit ? Visibility.Visible : Visibility.Collapsed;
|
|
@@ -44,23 +60,136 @@ public partial class GeofenceEditor : Window
|
|
State.Text = address.State;
|
|
State.Text = address.State;
|
|
PostCode.Text = Address.PostCode;
|
|
PostCode.Text = Address.PostCode;
|
|
|
|
|
|
|
|
+ Title = $"Geofence Editor ({(string.IsNullOrWhiteSpace(CoreUtils.GoogleAPIKey) ? "OSM" : "Google")})";
|
|
|
|
+
|
|
if (canEdit && string.IsNullOrWhiteSpace(Address.Geofence))
|
|
if (canEdit && string.IsNullOrWhiteSpace(Address.Geofence))
|
|
Task.Run(CheckAddress);
|
|
Task.Run(CheckAddress);
|
|
else
|
|
else
|
|
SetupMap();
|
|
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()
|
|
private async Task CheckAddress()
|
|
{
|
|
{
|
|
- IGeocoder geocoder = new GoogleGeocoder(CoreUtils.GoogleAPIKey);
|
|
|
|
- var matches = await geocoder.GeocodeAsync($"{Address.Street}, {Address.City} {Address.State} {Address.PostCode} Australia");
|
|
|
|
- var match = matches.FirstOrDefault();
|
|
|
|
|
|
+ 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}");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
|
|
- Address.Location.Longitude = match?.Coordinates.Longitude ?? 0.0;
|
|
|
|
- Address.Location.Latitude = match?.Coordinates.Latitude ?? 0.0;
|
|
|
|
- SquareFence(20.0);
|
|
|
|
- Address.Geofence = Serialization.Serialize(_definition);
|
|
|
|
|
|
|
|
Dispatcher.BeginInvoke(SetupMap);
|
|
Dispatcher.BeginInvoke(SetupMap);
|
|
|
|
|
|
@@ -79,13 +208,8 @@ public partial class GeofenceEditor : Window
|
|
|
|
|
|
private void SetupMap()
|
|
private void SetupMap()
|
|
{
|
|
{
|
|
- ImageryLayer.Center = new Point(Address.Location.Latitude, Address.Location.Longitude);
|
|
|
|
- ImageryLayer.Markers = new CoreObservableCollection<MapMarker>([new MapMarker()
|
|
|
|
- {
|
|
|
|
- Latitude = $"{Address.Location.Latitude:F15}",
|
|
|
|
- Longitude = $"{Address.Location.Longitude:F15}"
|
|
|
|
- }]);
|
|
|
|
-
|
|
|
|
|
|
+ CenterMap();
|
|
|
|
+
|
|
RadiusSlider.ValueChanged -= RadiusSliderChanged;
|
|
RadiusSlider.ValueChanged -= RadiusSliderChanged;
|
|
RadiusSlider.Value = 20.0;
|
|
RadiusSlider.Value = 20.0;
|
|
if (_definition.Coordinates.Count < 4)
|
|
if (_definition.Coordinates.Count < 4)
|
|
@@ -93,7 +217,17 @@ public partial class GeofenceEditor : Window
|
|
RadiusSlider.ValueChanged += RadiusSliderChanged;
|
|
RadiusSlider.ValueChanged += RadiusSliderChanged;
|
|
UpdateMap();
|
|
UpdateMap();
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ private void CenterMap()
|
|
|
|
+ {
|
|
|
|
+ ImageryLayer.Center = new Point(Address.Location.Latitude, Address.Location.Longitude);
|
|
|
|
+ ImageryLayer.Markers = new CoreObservableCollection<MapMarker>([new MapMarker()
|
|
|
|
+ {
|
|
|
|
+ Latitude = $"{Address.Location.Latitude:F15}",
|
|
|
|
+ Longitude = $"{Address.Location.Longitude:F15}"
|
|
|
|
+ }]);
|
|
|
|
+ }
|
|
|
|
+
|
|
private void RadiusSliderChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
|
private void RadiusSliderChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
|
{
|
|
{
|
|
RecalculateSquareFence();
|
|
RecalculateSquareFence();
|
|
@@ -126,27 +260,46 @@ public partial class GeofenceEditor : Window
|
|
Map.ZoomLevel = Math.Max(5, Map.ZoomLevel - 1);
|
|
Map.ZoomLevel = Math.Max(5, Map.ZoomLevel - 1);
|
|
UpdateMap();
|
|
UpdateMap();
|
|
}
|
|
}
|
|
-
|
|
|
|
- private bool _polygon = false;
|
|
|
|
- private bool _square = false;
|
|
|
|
|
|
+
|
|
|
|
+ private enum GeoFenceAction
|
|
|
|
+ {
|
|
|
|
+ None,
|
|
|
|
+ Polygon,
|
|
|
|
+ Square,
|
|
|
|
+ Coordinates
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private GeoFenceAction _action = GeoFenceAction.None;
|
|
|
|
+
|
|
|
|
+ private void UpdateButtons(GeoFenceAction action)
|
|
|
|
+ {
|
|
|
|
+ _action = action;
|
|
|
|
+ SetGeometry.IsEnabled = action == GeoFenceAction.None || action == GeoFenceAction.Polygon;
|
|
|
|
+ SetGeometry.Background = action == GeoFenceAction.Polygon
|
|
|
|
+ ? Brushes.Yellow
|
|
|
|
+ : Brushes.Silver;
|
|
|
|
+ SetRadius.IsEnabled = action == GeoFenceAction.None || action == GeoFenceAction.Square;
|
|
|
|
+ SetRadius.Background = action == GeoFenceAction.Square
|
|
|
|
+ ? Brushes.Yellow
|
|
|
|
+ : Brushes.Silver;
|
|
|
|
+ RadiusSlider.Visibility = action == GeoFenceAction.Square
|
|
|
|
+ ? Visibility.Visible
|
|
|
|
+ : Visibility.Collapsed;
|
|
|
|
+ SetCoordinates.IsEnabled = action == GeoFenceAction.None || action == GeoFenceAction.Coordinates;
|
|
|
|
+ SetCoordinates.Background = action == GeoFenceAction.Coordinates
|
|
|
|
+ ? Brushes.Yellow
|
|
|
|
+ : Brushes.Silver;
|
|
|
|
+ }
|
|
|
|
|
|
private void SetGeometry_OnClick(object sender, RoutedEventArgs e)
|
|
private void SetGeometry_OnClick(object sender, RoutedEventArgs e)
|
|
{
|
|
{
|
|
- if (!_polygon)
|
|
|
|
|
|
+ if (_action == GeoFenceAction.Polygon)
|
|
|
|
+ UpdateButtons(GeoFenceAction.None);
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- _square = false;
|
|
|
|
- SetRadius.Background = System.Windows.Media.Brushes.Silver;
|
|
|
|
- RadiusSlider.Visibility = Visibility.Collapsed;
|
|
|
|
-
|
|
|
|
- _polygon = true;
|
|
|
|
|
|
+ UpdateButtons(GeoFenceAction.Polygon);
|
|
_definition.Coordinates.Clear();
|
|
_definition.Coordinates.Clear();
|
|
UpdateMap();
|
|
UpdateMap();
|
|
- SetGeometry.Background = System.Windows.Media.Brushes.Yellow;
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- _polygon = false;
|
|
|
|
- SetGeometry.Background = System.Windows.Media.Brushes.Silver;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -155,42 +308,42 @@ public partial class GeofenceEditor : Window
|
|
var point = Mouse.GetPosition(Map);
|
|
var point = Mouse.GetPosition(Map);
|
|
var latlon = ImageryLayer.GetLatLonFromPoint(point);
|
|
var latlon = ImageryLayer.GetLatLonFromPoint(point);
|
|
var geopoint = new GeoPoint(latlon.Y, latlon.X);
|
|
var geopoint = new GeoPoint(latlon.Y, latlon.X);
|
|
-
|
|
|
|
- if (!_polygon)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetRadius_OnClick(object sender, RoutedEventArgs e)
|
|
private void SetRadius_OnClick(object sender, RoutedEventArgs e)
|
|
{
|
|
{
|
|
- if (!_square)
|
|
|
|
|
|
+ if (_action == GeoFenceAction.Square)
|
|
|
|
+ UpdateButtons(GeoFenceAction.None);
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- _polygon = false;
|
|
|
|
- SetGeometry.Background = System.Windows.Media.Brushes.Silver;
|
|
|
|
-
|
|
|
|
- _square = true;
|
|
|
|
|
|
+ UpdateButtons(GeoFenceAction.Square);
|
|
|
|
|
|
RadiusSlider.ValueChanged -= RadiusSliderChanged;
|
|
RadiusSlider.ValueChanged -= RadiusSliderChanged;
|
|
RadiusSlider.Value = 20.0;
|
|
RadiusSlider.Value = 20.0;
|
|
SquareFence(20.0);
|
|
SquareFence(20.0);
|
|
RadiusSlider.ValueChanged += RadiusSliderChanged;
|
|
RadiusSlider.ValueChanged += RadiusSliderChanged;
|
|
- RadiusSlider.Visibility = Visibility.Visible;
|
|
|
|
RecalculateSquareFence();
|
|
RecalculateSquareFence();
|
|
UpdateMap();
|
|
UpdateMap();
|
|
- SetRadius.Background = System.Windows.Media.Brushes.Yellow;
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- _square = false;
|
|
|
|
- RadiusSlider.Visibility = Visibility.Collapsed;
|
|
|
|
- SetRadius.Background = System.Windows.Media.Brushes.Silver;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -198,4 +351,12 @@ public partial class GeofenceEditor : Window
|
|
{
|
|
{
|
|
Task.Run(CheckAddress);
|
|
Task.Run(CheckAddress);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ private void SetCoordinates_OnClick(object sender, RoutedEventArgs e)
|
|
|
|
+ {
|
|
|
|
+ if (_action == GeoFenceAction.Coordinates)
|
|
|
|
+ UpdateButtons(GeoFenceAction.None);
|
|
|
|
+ else
|
|
|
|
+ UpdateButtons(GeoFenceAction.Coordinates);
|
|
|
|
+ }
|
|
}
|
|
}
|