Using What3Words in Xamarin.Forms

What3Words is a project that has digitally mapped the earth into 3 meter x 3 meter squares. Each square is identified by three random words. Basically giving an ‘address’ to everywhere on earth. You can use the user’s current What3Words address in your Xamarin.Forms app. Here’s how:

Here is a quick HTTPClientService that calls the What3Words API. Replace Config.What3WordsAPIKey with your API Key.

using Microsoft.AppCenter.Crashes;
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;

namespace MyHelpers
{
    public static class HTTPClientService
    {
        public static async Task<ExternalAPIModels.What3WordsModel.RootObject> RefreshWhat3WordsDataAsync(
            double longitude, double latitude)
        {
            var locData = new What3WordsModel.RootObject();

            try
            {
                HttpClient client = new HttpClient();
                client.MaxResponseContentBufferSize = 256000;
                var urlStr = $"https://api.what3words.com/v2/reverse?coords={latitude}%2C{longitude}&amp;key={Config.What3WordsAPIKey}";
                var uri = new Uri(urlStr);
                var response = await client.GetAsync(uri);
                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    locData = JsonConvert.DeserializeObject<What3WordsModel.RootObject>(content);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(@"ERROR {0}", ex.Message);
                //Crashes.TrackError(ex); //uncomment if you are using AppCenter Crash Reporting
            }

            return locData;
        }
    }
}

You will need the models for the What3Words API.

namespace What3WordsModel
{
    public class Bounds
    {
        public Northeast northeast { get; set; }
        public Southwest southwest { get; set; }
    }

    public class Crs
    {
        public Properties properties { get; set; }
        public string type { get; set; }
    }

    public class Geometry
    {
        public double lat { get; set; }
        public double lng { get; set; }
    }

    public class Northeast
    {
        public double lat { get; set; }
        public double lng { get; set; }
    }

    public class Properties
    {
        public string href { get; set; }
        public string type { get; set; }
    }

    public class RootObject
    {
        public Bounds bounds { get; set; }
        public Crs crs { get; set; }
        public Geometry geometry { get; set; }
        public string language { get; set; }
        public string map { get; set; }
        public Status status { get; set; }
        public string thanks { get; set; }
        public string words { get; set; }
    }

    public class Southwest
    {
        public double lat { get; set; }
        public double lng { get; set; }
    }

    public class Status
    {
        public string reason { get; set; }
        public int status { get; set; }
    }
}

In your viewmodel, you can use Xamarin.Essentials Geolocation plugin to get the users location and feed that into the What3Words API call.

private async Task Refresh()
        {
            try
            {
                //Option #1
                //This is faster then doing a full query, but can be less accurate.
                var locationLast = await Geolocation.GetLastKnownLocationAsync();

                if (locationLast != null)
                {
                    LastLocation = $"Latitude: {locationLast.Latitude}, Longitude: {locationLast.Longitude}, Altitude: {locationLast.Altitude}";
                    await setW3WLastLocation(locationLast.Longitude, locationLast.Latitude);
                }

                //Option #2
                //This takes a little longer, but queries the real-time location of the user.
                var request = new GeolocationRequest(GeolocationAccuracy.Medium);
                var locationRealtime = await Geolocation.GetLocationAsync(request);

                if (locationRealtime != null)
                {
                    RealTimeLocation = $"Latitude: {locationRealtime.Latitude}, Longitude: {locationRealtime.Longitude}, Altitude: {locationRealtime.Altitude}";
                }
            }
            catch (FeatureNotSupportedException fnsEx)
            {
                // Handle not supported on device exception
                Crashes.TrackError(fnsEx);
            }
            catch (FeatureNotEnabledException fneEx)
            {
                // Handle not enabled on device exception
                Crashes.TrackError(fneEx);
            }
            catch (PermissionException pEx)
            {
                // Handle permission exception
                Crashes.TrackError(pEx);
            }
            catch (Exception ex)
            {
                // Unable to get location
                Crashes.TrackError(ex);
            }
        }

        private async Task setW3WLastLocation(double longitude, double latitude)
        {
            var w3w = await Helpers.HTTPClientService.RefreshWhat3WordsDataAsync(longitude, latitude);
            if (w3w.status.status == 200)
            {
                What3WordsLocation = w3w.words;
            }
        }

        public string What3WordsLocation
        {
            get { return _what3WordsLocation; }
            set { Set(nameof(What3WordsLocation), ref _what3WordsLocation, value); }
        }

        public string RealTimeLocation
        {
            get { return _realTimeLocation; }
            set { Set(nameof(RealTimeLocation), ref _realTimeLocation, value); }
        }

        public string LastLocation
        {
            get { return _lastLocation; }
            set { Set(nameof(LastLocation), ref _lastLocation, value); }
        }

        private string _lastLocation;
        private string _realTimeLocation;
        private string _what3WordsLocation;

Leave a Comment

Your email address will not be published. Required fields are marked *