Xamarin.Forms Maps

Xamarin.Forms Maps, allows you to display a map inside your Xamarin.Forms application. You can see a map of your current location, place pins on the map, and also provide a route between two locations. I have a sample repo at XamarinMaps, if you want to see a complete solution.

Setup

To setup your projects, first install the Xamarin.Forms.Maps nuget package into all of your projects, including your Class Library.

Android

Place this in your MainActivity.cs, just below Xamarin.Forms.Forms.Init(this, bundle);

FormsMaps.Init(this, bundle);

iOS

Place this in your AppDelegate.cs, just below Xamarin.Forms.Forms.Init();

FormsMaps.Init();

UWP

Place this in your App.xaml.cs, just below Xamarin.Forms.Forms.Init(e);

FormsMaps.Init("AUTHENTICATION_TOKEN");

I will mention how, to get this authentication token next, along with other configuration needed.

Map Configuration

Android

Android uses Google Maps, and to use this, you will need an API Key. Follow the instructions in Google Maps API Key For Xamarin.Android App to get yours. Then in your AndroidManifest.xml, add the following.

<meta-data android:name="com.google.android.maps.v2.API_KEY" 
           android:value="YOUR_API_KEY" />

Note: You will need to generate an API Key for your Debug and Release versions of your app.

You will also need to ensure, you enable these permissions.

  • ACCESS_COARSE_LOCATION
  • ACCESS_FINE_LOCATION
  • ACCESS_LOCATION_EXTRA_COMMANDS
  • ACCESS_MOCK_LOCATION
  • ACCESS_NETWORK_STATE
  • ACCESS_WIFI_STATE
  • INTERNET

iOS

iOS uses Apple Maps, and it is common knowledge that they aren’t as good as Google Maps. If you are displaying a simple map, doing a simple route or dropping a few pins, Apple Maps should suffice. If you are doing something complicated, you might want to use Google Maps. Using Google Maps on iOS is out of the scope of this post, but you can use the Xamarin.Forms.GoogleMaps Nuget package to achieve this.

For iOS 8 and above, you need to add some values into your Info.plist. This is to comply with permission dialogs, when iOS tries to load the map.

<key>NSLocationAlwaysUsageDescription</key>
<string>AppName wants to use your location for ...?</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>AppName is using your location.</string>

UWP

UWP uses Bing Maps. You will need to create an Authentication Token. Follow the instructions in Request a maps authentication key, and replace the string in the FormMaps.Init(“AUTHENTICATION_TOKEN”);.

Go to your Package.appxmanifest and in the Capabilities tab, set the Location permission.

Create A Map

We can create a map in XAML, with the following code.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"             
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"             
             xmlns:local="clr-namespace:XamarinMaps"
             xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"             
             x:Class="XamarinMaps.MainPage">
    <maps:Map MapType="Street" />
</ContentPage>

The MapType, allows you to select Street, Satellite or Hybrid.

Bindable Map Control

The default maps control, doesn’t contain many nice bindable properties. Hence, lets create a new Map control, with some bindable properties. These will add Pins to the map, and center the map to a particular position.

public class BindableMap : Map {
    public static readonly BindableProperty MapPinsProperty = BindableProperty.Create( 
             nameof(Pins), 
             typeof(ObservableCollection<Pin>),
             typeof(BindableMap), 
             new ObservableCollection<Pin>(),
             propertyChanged: (b, o, n) =>
             { 
                 var bindable = (BindableMap)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(BindableMap), 
             new Position(0, 0), 
             propertyChanged: (b, o, n) =>
             {  
                 ((BindableMap)b).MoveToRegion(MapSpan.FromCenterAndRadius( 
                      (Position)n,
                      Distance.FromMiles(1)));
             }); 

    public Position MapPosition { get; set; } 
}

You can now easily reference this control, with this XAML.

<local:BindableMap MapType="Street" MapPosition="{Binding MyPosition}" MapPins="{Binding PinCollection}" />

Current Location

To show the map at your current location, you will need to get the location from your mobile device and pass it to the map. I recommend installing the Geolocator plugin, to retrieve this information. Once installed, follow the instructions to ensure you have the correct permissions setup.

var position = await Plugin.Geolocator.CrossGeolocator.Current.GetPositionAsync(); 
MyPosition = new Position(position.Latitude, position.Longitude);

Then you can pass this latitude and longitude, to the BindableMap. This code assumes you bind MyPosition to the Position property of your BindableMap.

private Position _myPosition= new Position(-37.8141, 144.9633); 
public Position MyPosition { get { return _myPosition; } set { _myPosition = value; OnPropertyChanged(); } }

Place a Pin

If you want to place a pin on the map, this is easy. Bind the MapPins property to your ViewModel, and add as many pins as you want. Create a property in your ViewModel and bind to MapPins in BindableMap.

private ObservableCollection<Pin> _pinCollection = new ObservableCollection<Pin>(); 
public ObservableCollection<Pin> PinCollection { get { return _pinCollection; } set { _pinCollection = value; OnPropertyChanged(); } }

Then you can add the Pin, like this. Please note, the label property is required.

PinCollection.Add(new Pin() { Position = MyPosition, Type = PinType.Generic, Label ="I'm a Pin" });

Xamarin has documentation on how to customize a map pin, if you further want to integrate the map into your app.

Create a Route

If you want to create a route between two locations on your map, this can be done. It does require custom renderers and a bit more effort. Xamarin has the full documentation and samples in Highlighting a Route on a Map.


XAMARIN.FORMS MONTHLY NEWSLETTER

JOIN 1,100+ SUBSCRIBERS

  1. Don't miss out on updates
  2. The latest info from this site, Xamarin and the community
  3. Unsubscribe at any time*

* We use MailChimp, with double opt-in, and instant unsubscribe

Microsoft MVP | Xamarin MVP | Xamarin Certified Developer | Exrin MVVM Framework | Xamarin Forms Developer | Melbourne, Australia

Related Posts

Tagged:Tags:

2 Comments

  1. Divya

    Hello, I have a problem in Xamarin.IOS. Actually, I am getting possible addresses to the current location in Xamarin.Android. See the below code….I am getting the list of possible addresses in “list_of_addresses ” variable in Android,But, In IOS, getting different data in this same variable while debugging.

    please solve this issue ASAP.and please provide the solution for “How to get possible addresses (nearest locations) to the current address.”
    ——————————————————————————

    private async void Address_list_ClickedAsync(object sender, EventArgs e)
    {
    DisplayAlert(“MapAlert”, “Multiple addresses near to Current location”, “Ok”);
    var position = await CrossGeolocator.Current.GetPositionAsync(timeoutMilliseconds: 100000);
    // string addres=await geoCoder.GetAddressesForPositionAsync(position);
    CrossGeolocator.Current.DesiredAccuracy = 100;
    google_map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(position.Latitude, position.Longitude), Distance.FromMiles(3)));
    var pos = new Position(position.Latitude, position.Longitude);

    //Multiple Markers with current location Marker also
    var pin = new Pin
    {
    Type = PinType.Place,
    Position = pos,
    Label = “CurrentLocation”,
    Address = “Address Info”

    };
    google_map.Pins.Add(pin);

    IEnumerable list_of_addresses = await geoCoder.GetAddressesForPositionAsync(pos);
    list_of_addresses.Count();

    for (int i=0;i<list_of_addresses.Count();i++)
    {
    var listt=list_of_addresses.ToList();
    string each_address = listt[i];
    var list_of_geopints= await geoCoder.GetPositionsForAddressAsync(each_address);
    foreach(var geopint in list_of_geopints)
    {
    Position position_ = new Position(geopint.Latitude,geopint.Longitude);
    if (Device.OS == TargetPlatform.Android)
    {

    var pins = new Pin()
    {
    Type = PinType.Place,
    Position = position_,
    Label = each_address,
    Address = each_address
    };
    google_map.Pins.Add(pins);
    }
    if (Device.OS == TargetPlatform.iOS)
    {

    var pins = new Pin()
    {
    Type = PinType.Place,
    Position = position_,
    Label = each_address,

    };
    google_map.Pins.Add(pins);
    }

    }
    ————————————————

    Thanks & Regards,
    Divya.

  2. anton

    Unhandled Exception:

    Java.Lang.SecurityException: my location requires permission ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION