From 17fdde66d94155fc62a034fa6658995bef6fd6e5 Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 22 Mar 2016 13:02:25 -0700 Subject: Initial import --- Xamarin.Forms.Maps.UWP/MapRenderer.cs | 297 ++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 Xamarin.Forms.Maps.UWP/MapRenderer.cs (limited to 'Xamarin.Forms.Maps.UWP/MapRenderer.cs') diff --git a/Xamarin.Forms.Maps.UWP/MapRenderer.cs b/Xamarin.Forms.Maps.UWP/MapRenderer.cs new file mode 100644 index 00000000..4d3564e7 --- /dev/null +++ b/Xamarin.Forms.Maps.UWP/MapRenderer.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Threading.Tasks; +using Windows.Devices.Geolocation; +using Windows.UI; +using Windows.UI.Xaml.Controls.Maps; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Shapes; +#if WINDOWS_UWP +using Xamarin.Forms.Platform.UWP; + +#else +using Xamarin.Forms.Platform.WinRT; + +#endif + +#if WINDOWS_UWP + +namespace Xamarin.Forms.Maps.UWP +#else + +namespace Xamarin.Forms.Maps.WinRT +#endif +{ + public class MapRenderer : ViewRenderer + { + protected override async void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + if (e.OldElement != null) + { + var mapModel = e.OldElement; + MessagingCenter.Unsubscribe(this, "MapMoveToRegion"); + ((ObservableCollection)mapModel.Pins).CollectionChanged -= OnCollectionChanged; + } + + if (e.NewElement != null) + { + var mapModel = e.NewElement; + + if (Control == null) + { + SetNativeControl(new MapControl()); + Control.MapServiceToken = FormsMaps.AuthenticationToken; + Control.ZoomLevelChanged += async (s, a) => await UpdateVisibleRegion(); + Control.CenterChanged += async (s, a) => await UpdateVisibleRegion(); + } + + MessagingCenter.Subscribe(this, "MapMoveToRegion", async (s, a) => await MoveToRegion(a), mapModel); + + UpdateMapType(); + UpdateHasScrollEnabled(); + UpdateHasZoomEnabled(); + + ((ObservableCollection)mapModel.Pins).CollectionChanged += OnCollectionChanged; + + if (mapModel.Pins.Any()) + LoadPins(); + + await UpdateIsShowingUser(); + } + } + + protected override async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == Map.MapTypeProperty.PropertyName) + UpdateMapType(); + else if (e.PropertyName == Map.IsShowingUserProperty.PropertyName) + await UpdateIsShowingUser(); + else if (e.PropertyName == Map.HasScrollEnabledProperty.PropertyName) + UpdateHasScrollEnabled(); + else if (e.PropertyName == Map.HasZoomEnabledProperty.PropertyName) + UpdateHasZoomEnabled(); + } + + protected override void Dispose(bool disposing) + { + if (disposing && !_disposed) + { + _disposed = true; + + MessagingCenter.Unsubscribe(this, "MapMoveToRegion"); + + if (Element != null) + ((ObservableCollection)Element.Pins).CollectionChanged -= OnCollectionChanged; + } + base.Dispose(disposing); + } + + bool _disposed; + bool _firstZoomLevelChangeFired; + Ellipse _userPositionCircle; + + void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (Pin pin in e.NewItems) + LoadPin(pin); + break; + case NotifyCollectionChangedAction.Move: + // no matter + break; + case NotifyCollectionChangedAction.Remove: + foreach (Pin pin in e.OldItems) + RemovePin(pin); + break; + case NotifyCollectionChangedAction.Replace: + foreach (Pin pin in e.OldItems) + RemovePin(pin); + foreach (Pin pin in e.NewItems) + LoadPin(pin); + break; + case NotifyCollectionChangedAction.Reset: + ClearPins(); + break; + } + } + + void LoadPins() + { + foreach (var pin in Element.Pins) + LoadPin(pin); + } + + void ClearPins() + { + Control.Children.Clear(); + UpdateIsShowingUser(); + } + + void RemovePin(Pin pinToRemove) + { + var pushPin = Control.Children.FirstOrDefault(c => + { + var pin = (c as PushPin); + return (pin != null && pin.DataContext.Equals(pinToRemove)); + }); + + if (pushPin != null) + Control.Children.Remove(pushPin); + } + + void LoadPin(Pin pin) + { + Control.Children.Add(new PushPin(pin)); + } + + async Task UpdateIsShowingUser() + { + if (Element.IsShowingUser) + { + var myGeolocator = new Geolocator(); + if (myGeolocator.LocationStatus != PositionStatus.NotAvailable && + myGeolocator.LocationStatus != PositionStatus.Disabled) + { + var userPosition = await myGeolocator.GetGeopositionAsync(); + if (userPosition?.Coordinate != null) + LoadUserPosition(userPosition.Coordinate, true); + } + } + else if (_userPositionCircle != null && Control.Children.Contains(_userPositionCircle)) + Control.Children.Remove(_userPositionCircle); + } + + async Task MoveToRegion(MapSpan span, MapAnimationKind animation = MapAnimationKind.Bow) + { + var nw = new BasicGeoposition + { + Latitude = span.Center.Latitude + span.LatitudeDegrees / 2, + Longitude = span.Center.Longitude - span.LongitudeDegrees / 2 + }; + var se = new BasicGeoposition + { + Latitude = span.Center.Latitude - span.LatitudeDegrees / 2, + Longitude = span.Center.Longitude + span.LongitudeDegrees / 2 + }; + var boundingBox = new GeoboundingBox(nw, se); + await Control.TrySetViewBoundsAsync(boundingBox, null, animation); + } + + async Task UpdateVisibleRegion() + { + if (Control == null || Element == null) + return; + + if (!_firstZoomLevelChangeFired) + { + await MoveToRegion(Element.LastMoveToRegion, MapAnimationKind.None); + _firstZoomLevelChangeFired = true; + return; + } + Geopoint nw, se = null; + try + { + Control.GetLocationFromOffset(new Windows.Foundation.Point(0, 0), out nw); + Control.GetLocationFromOffset(new Windows.Foundation.Point(Control.ActualWidth, Control.ActualHeight), out se); + } + catch (Exception) + { + return; + } + + if (nw != null && se != null) + { + var boundingBox = new GeoboundingBox(nw.Position, se.Position); + var center = new Position(boundingBox.Center.Latitude, boundingBox.Center.Longitude); + var latitudeDelta = Math.Abs(center.Latitude - boundingBox.NorthwestCorner.Latitude); + var longitudeDelta = Math.Abs(center.Longitude - boundingBox.NorthwestCorner.Longitude); + Element.VisibleRegion = new MapSpan(center, latitudeDelta, longitudeDelta); + } + } + + void LoadUserPosition(Geocoordinate userCoordinate, bool center) + { + var userPosition = new BasicGeoposition + { + Latitude = userCoordinate.Point.Position.Latitude, + Longitude = userCoordinate.Point.Position.Longitude + }; + + var point = new Geopoint(userPosition); + + if (_userPositionCircle == null) + { + _userPositionCircle = new Ellipse + { + Stroke = new SolidColorBrush(Colors.White), + Fill = new SolidColorBrush(Colors.Blue), + StrokeThickness = 2, + Height = 20, + Width = 20, + Opacity = 50 + }; + } + + if (Control.Children.Contains(_userPositionCircle)) + Control.Children.Remove(_userPositionCircle); + + MapControl.SetLocation(_userPositionCircle, point); + MapControl.SetNormalizedAnchorPoint(_userPositionCircle, new Windows.Foundation.Point(0.5, 0.5)); + + Control.Children.Add(_userPositionCircle); + + if (center) + { + Control.Center = point; + Control.ZoomLevel = 13; + } + } + + void UpdateMapType() + { + switch (Element.MapType) + { + case MapType.Street: + Control.Style = MapStyle.Road; + break; + case MapType.Satellite: + Control.Style = MapStyle.Aerial; + break; + case MapType.Hybrid: + Control.Style = MapStyle.AerialWithRoads; + break; + } + } + +#if WINDOWS_UWP + void UpdateHasZoomEnabled() + { + Control.ZoomInteractionMode = Element.HasZoomEnabled + ? MapInteractionMode.GestureAndControl + : MapInteractionMode.ControlOnly; + } + + void UpdateHasScrollEnabled() + { + Control.PanInteractionMode = Element.HasScrollEnabled ? MapPanInteractionMode.Auto : MapPanInteractionMode.Disabled; + } +#else + void UpdateHasZoomEnabled() + { + } + + void UpdateHasScrollEnabled() + { + } +#endif + } +} \ No newline at end of file -- cgit v1.2.3