Cómo crear Mapas bindable en Xamarin.Forms (Parte 2)

Si estas desarrollando una plicación Xamarin.Forms y vas a utilizar mapas Xamarin.Forms.Maps, te permite visualizar un mapa dentro de una aplicación Xamarin.Forms. En este artículo veremos cómo mostrar varias ubicaciones, mostrar la ubicación actual y como generar una ruta. Puedes leer la primera parte en Cómo crear Mapas bindable en Xamarin.Forms (Parte 1)
Control de mapa enlazable (bindable)
El control de mapas predeterminado no contiene muchas propiedades y no se puede enlazar o bindar. Por lo tanto, es necesario crear un nuevo control de mapa, con algunas propiedades enlazables. Estos agregarán Pins al mapa, centrará el mapa en una posición particular y permitirá crear una o n rutas.
public class CustomMap : Map { private List<Position> routeCoordinates = new List<Position>(); public List<Position> RouteCoordinates { get { return routeCoordinates; } set{ routeCoordinates = value; } } public static readonly BindableProperty RouteCoordinatesProperty = BindableProperty.Create( nameof(RouteCoordinates), typeof(ObservableCollection<Position>), typeof(CustomMap), new ObservableCollection<Position>(), BindingMode.TwoWay, propertyChanged: (b, o, n) => { var bindable = (CustomMap)b; var collection = (ObservableCollection<Position>)n; foreach (var item in collection) { bindable.RouteCoordinates.Add(item); } collection.CollectionChanged += (sender, e) => { Device.BeginInvokeOnMainThread(() => { switch (e.Action) { case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Remove: if (e.OldItems != null) foreach (var item in e.OldItems) bindable.RouteCoordinates.Remove((Position)item); if (e.NewItems != null) foreach (var item in e.NewItems) bindable.RouteCoordinates.Add((Position)item); break; case NotifyCollectionChangedAction.Reset: bindable.RouteCoordinates.Clear(); break; } }); }; } ); public static readonly BindableProperty MapPinsProperty = BindableProperty.Create( nameof(Pins), typeof(ObservableCollection<Pin>), typeof(CustomMap), new ObservableCollection<Pin>(), propertyChanged: (b, o, n) => { var bindable = (CustomMap)b; bindable.Pins.Clear(); var collection = (ObservableCollection<Pin>)n; foreach (var item in collection) bindable.Pins.Add(item); collection.CollectionChanged += (sender, e) => { Device.BeginInvokeOnMainThread(() => { switch (e.Action) { case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Remove: if (e.OldItems != null) foreach (var item in e.OldItems) bindable.Pins.Remove((Pin)item); if (e.NewItems != null) foreach (var item in e.NewItems) bindable.Pins.Add((Pin)item); break; case NotifyCollectionChangedAction.Reset: bindable.Pins.Clear(); break; } }); }; }); public IList<Pin> MapPins { get; set; } public static readonly BindableProperty MapPositionProperty = BindableProperty.Create( nameof(MapPosition), typeof(Position), typeof(CustomMap), new Position(0, 0), propertyChanged: (b, o, n) => { ((CustomMap)b).MoveToRegion(MapSpan.FromCenterAndRadius( (Position)n, Distance.FromMiles(1))); }); public Position MapPosition { get; set; } }
Ubicación actual
Para mostrar en el mapa la ubicación actual, deberás obtener la ubicación desde tu dispositivo móvil y pasarlo al mapa. Recomiendo instalar el paquete Nuguet Geolocator para recuperar esta información. Una vez instalado, sigue las instrucciones para asegurar tener la configuración de permisos correcta.
var position = await Plugin.Geolocator.CrossGeolocator.Current.GetPositionAsync(); MyPosition = new Position(position.Latitude, position.Longitude);
Con lo anterior puedes pasar la latitud y longitud, al control CustomMap. No te olvides de declarar la propiedad MyPosition y así vincularMyPosition a la propiedad Position de tu CustomMap.
private Position myPosition = new Position(19.3188895, -99.10986379999997); public Position MyPosition{ get{ return myPosition; } set{ myPosition=value; OnPropertyChanged("MyPosition"); } }
Colocar uno más Pines
Si quieres colocar un pin en el mapa, es fácil. Enlaza la propiedad MapPins a su ViewModel, y agrega tantos pines como desees. Crea una propiedad en tu ViewModel y se lligalo al MapPins en CustomMap.
private ObservableCollection<Pin> allPines = new ObservableCollection<Pin>(); public ObservableCollection<Pin> AllPines { get { return allPines; } set { allPines = value; OnPropertyChanged("AllPines"); } }
Entonces puedes agregar los Pines, así. Ten en cuenta que la propiedad de la etiqueta es obligatoria.
AllPines.Add(new Pin() { Position = new Position(37.797534, -122.401827), Type = PinType.Generic, Label = "Pin Bindable" });
Crea una ruta
Para crear una ruta debes pasarle las posiciones que va a pintar la línea. Debes crear la propiedad enlazable.
private ObservableCollection<Position> ruta = new ObservableCollection<Position>(); public ObservableCollection<Position> Ruta { get { return ruta; } set { ruta = value; OnPropertyChanged("Ruta"); } }
Después debes agregar todos los puntos de donde se va agregar la ruta.
Ruta.Add(new Position(37.797534, -122.401827)); Ruta.Add(new Position(37.797510, -122.402060)); Ruta.Add(new Position(37.790269, -122.400589)); Ruta.Add(new Position(37.790265, -122.400474)); Ruta.Add(new Position(37.790228, -122.400391)); Ruta.Add(new Position(37.790126, -122.400360)); Ruta.Add(new Position(37.789250, -122.401451)); Ruta.Add(new Position(37.788440, -122.400396)); Ruta.Add(new Position(37.787999, -122.399780)); Ruta.Add(new Position(37.786736, -122.398202)); Ruta.Add(new Position(37.786345, -122.397722)); Ruta.Add(new Position(37.785983, -122.397295)); Ruta.Add(new Position(37.785559, -122.396728)); Ruta.Add(new Position(37.780624, -122.390541)); Ruta.Add(new Position(37.777113, -122.394983)); Ruta.Add(new Position(37.776831, -122.394627));
Para que las rutas funcionen se debe crear una clase en los proyectos iOS y Android para que se muestren las líneas de la ruta.