In this article, we will learn how to find the latitude and longitude of a location in C# using the Google Maps Geocoding API. We’ll introduce two different methods for obtaining the coordinates: using the GoogleMaps.LocationServices
NuGet package, which simplifies the process but can be less flexible, and using HttpClient
directly, which provides us more control over HTTP requests and responses.
So, let’s begin.
Get an API Key and Configure the Project
First, to get the latitude and longitude of an address, we’ll need to get an API key from Google. To do this, we visit Google’s developer documentation for instructions on getting one. It’s also important to secure our API key to ensure no one else can use it. To accomplish this, we will store our API key using the Secret Manager tool to keep it safe.
Next, let’s define an address for our testing:
var address = "Miami, Florida";
Now, let’s create a simple console application and set up our ConfigurationBuilder
:
public class Program { static async Task Main(string[] args) { var builder = new ConfigurationBuilder() .AddUserSecrets<Program>(); var configuration = builder.Build(); var apiKey = configuration["Secret:MyApiKey"]; if (string.IsNullOrEmpty(apiKey)) { Console.WriteLine("API key not found."); return; } } }
This configuration builder helps us get the API key from the secret manager. Here, we initialize a ConfigurationBuilder
, and then add user secrets to the Program
class. We then call Build()
to build our configuration. Our configuration object now has the settings from our user secrets.
Next, we retrieve the API key value from the configuration using the key "Secret:MyApiKey"
(We can name this whatever we call our API key in the secret manager). Next, we verify that we have successfully loaded our API key (i.e. validate its existence), by ensuring it’s not null or empty.
Now, we can begin getting our coordinates!
Find the Latitude and Longitude of a Location via GoogleMaps.LocationServices NuGet Package
First, we install the GoogleMaps.LocationServices NuGet package:
dotnet add package GoogleMaps.LocationServices
The Google Maps Location Services package allows our app to communicate with Google Maps for location services, which includes getting the latitude and longitude.
We start by defining the model:
public class CustomMapPoint { [Required] public double Latitude { get; set; } [Required] public double Longitude { get; set; } }
Here, we define a CustomMapPoint
class. We will use this class to return a latitude and a longitude in our methods. The [Required]
attribute ensures that Longitude
and Latitude
are provided to help us avoid situations where we have incomplete or incorrect location data.
Implementing the Service
Next, we create an interface that tells our app what a location service should do:
public interface IMapLocationService { Task<CustomMapPoint> GetLatLongFromAddressAsync(string address); }
Here, the IMapLocationService
interface allows us to use different mapping services if needed, not just Google Maps, without changing much of our code. Within our new interface, we define the method GetLatLongFromAddressAsync()
, which takes an address as a parameter and asynchronously returns a CustomMapPoint
object.
Now, we wrap Google’s location service into a class that implements our interface:
public class GoogleLocationServiceWrapper : IMapLocationService { private readonly GoogleLocationService _glsContext; public GoogleLocationServiceWrapper(string apiKey) { _glsContext = new GoogleLocationService(apiKey); } public async Task<CustomMapPoint> GetLatLongFromAddressAsync(string address) { var location = await Task.Run(() => _glsContext.GetLatLongFromAddress(address)); return new CustomMapPoint() { Latitude = location.Latitude, Longitude = location.Longitude }; } }
Here, we declare our class GoogleLocationServiceWrapper
which implements the IMapLocationService
interface. Then, we declare a private readonly
field _glsContext
with the type GoogleLocationService
. This field will hold an instance of Google’s location service that we’ll use to get the coordinates.
In the constructor, we initialize _glsContext
with a new instance of GoogleLocationService
passing an API key as a parameter. We do this to authenticate requests to Google’s location service.
We then implement the asynchronous method GetLatLongFromAddressAsync()
from our interface. In the method, we use the _glsContext
instance to call GetLatLongFromAddress()
, passing in our address. We also wrap this call in Task.Run()
, making it asynchronous and thus preventing it from blocking the main execution thread. This method then communicates with Google’s location service and returns a location
object containing our latitude and longitude values.
Lastly, we create and return a new CustomMapPoint
object, setting its Latitude
and Longitude
properties to the values from the location
object.
Getting the Latitude and Longitude
After installing the Google Maps Location Services package, setting up the CustomMapPoint
model, IMapLocationService
interface and creating a GoogleLocationServiceWrapper
, we can now get our coordinates:
public class LatLongWithNuGet { private readonly IMapLocationService _locationService; public LatLongWithNuGet(IMapLocationService locationService) { _locationService = locationService; } public async Task<string> GetLatLongWithNuGet(string address) { try { var latlong = await _locationService.GetLatLongFromAddressAsync(address); return $"Address ({address}) is at {latlong.Latitude}, {latlong.Longitude}"; } catch (Exception ex) { return $"Error retrieving coordinates: {ex.Message}"; } } }
Here, we create the LatLongWithNuGet
class. This class uses a private readonly
field for the IMapLocationService
, making sure that the service we provided to the class at the start doesn’t change later on.
Then, the class constructor takes an IMapLocationService
as the type of parameter, allowing the class to work with any service that follows this interface.
Next, in the GetLatLongWithNuGet
method, we try to get the coordinates of an address asynchronously. If the location service successfully finds the address, it returns the coordinates. If not, the catch block catches any errors and returns an error message.
Finally, in Program.cs
, we can get the latitude and longitude:
var locationService = new GoogleLocationServiceWrapper(apiKey); var latLongWithNuGet = new LatLongWithNuGet(locationService); var coordinatesNuGet = await latLongWithNuGet.GetLatLongWithNuGet(address); Console.WriteLine(coordinatesNuGet);
First, we create locationService
using GoogleLocationServiceWrapper
and our API key, connecting us to Google’s location services. Then, we initialize a LatLongWithNuGet
instance passing in our locationService
object. Finally, we asynchronously retrieve our coordinates by calling the GetLatLongWithNuGet()
method on the latLongWithNuGet
instance:
Address (Miami, Florida) is at 25.7616798, -80.1917902
Even though we have the latitude and longitude, let’s try one other way of getting the coordinates using HttpClient
directly.
Find the Latitude and Longitude of a Location via HttpClient
Here, we’re using HttpClient
to get the latitude and longitude of Miami, Florida by communicating with Google’s Geocoding API. One advantage of using HttpClient
directly is that it offers more control and flexibility. Additionally, to better manage HttpClient
, we will use the IHttpClientFactory
interface.
Setting Up HttpClient for Geocoding
Since we are using IHttpClientFactory
, we first add the Microsoft.Extensions.Http
package:
dotnet add package Microsoft.Extensions.Http
This package introduces IHttpClientFactory
, which helps us better manage HttpClient
instances in our projects. It’s a game-changer for creating and using HttpClient
in a way that improves application performance and reliability.
We use IHttpClientFactory
because directly instantiating HttpClient
can lead to challenges like socket exhaustion and slow performance.
IHttpClientFactory
, take a look at our other article Using HttpClientFactory in ASP.NET Core Applications.After we add the package, we can move on to create our LatLongWithHttpClient
class:
public class LatLongWithHttpClient : IMapLocationService { private readonly IHttpClientFactory _httpClientFactory; private readonly string _apiKey; public LatLongWithHttpClient(IHttpClientFactory httpClientFactory, string apiKey) { _httpClientFactory = httpClientFactory; _apiKey = apiKey; } }
Here, in our LatLongWithHttpClient
class, we implement the IMapLocationService
interface that we created previously. This is to make sure our code is compatible with different mapping services. This class includes private readonly
fields _httpClientFactory
and _apiKey
. _httpClientFactory
is used for creating instances of HttpClient
for web requests to Google’s Geocoding API. Both fields are private
and readonly
because they are only needed within this class and shouldn’t change once we initialize them. We then initialize these fields in the constructor.
Creating Our Geocoding Method
Let’s move on to our GetLatLongWithAddressAsync
method:
public async Task<CustomMapPoint> GetLatLongFromAddressAsync(string address) { var httpClient = _httpClientFactory.CreateClient("MapsClient"); var relativeUri = $"maps/api/geocode/json?address={Uri.EscapeDataString(address)}&key={_apiKey}"; var response = await httpClient.GetAsync(relativeUri); response.EnsureSuccessStatusCode(); var root = await response.Content.ReadFromJsonAsync<JsonElement>(); var status = root.GetProperty("status").GetString(); // ... continued after explanation }
Here, the GetLatLongWithAddressAsync
method uses async tasks to find the latitude and longitude using Google’s Geocoding API. We start by using _httpClientFactory.CreateClient("MapsClient")
to get an instance of HttpClient
that’s set up for mapping services. This instance is assigned to the httpClient
variable. The "MapsClient"
refers to the settings we define later in Program.cs
, which will include the base URL for Google’s Geocoding API.
After that, we initialize relativeUri
with the address and API key.
Then, we send a GET request with httpClient.GetAsync(relativeUri)
and wait for the response. After checking for a successful status with response.EnsureSuccessStatusCode()
, we parse the JSON response and assign it to the variable root
.
If the response status
is “OK”, we go through root
to get the latitude and longitude:
if (status == "OK") { var location = root.GetProperty("results")[0].GetProperty("geometry").GetProperty("location"); return new CustomMapPoint { Latitude = location.GetProperty("lat").GetDouble(), Longitude = location.GetProperty("lng").GetDouble() }; } else { throw new Exception($"Error retrieving coordinates: '{status}'."); }
First, root.GetProperty("results")[0]
allows us to access the first item in the “results” array.
Then, .GetProperty("geometry").GetProperty("location")
reaches the “location” object, where the geographic data is found, and assigns the data to the location
variable.
After that, we create and return a new CustomMapPoint
object, setting its Latitude
and Longitude
properties using location.GetProperty("lat").GetDouble()
and location.GetProperty("lng").GetDouble()
.
However, if the status
isn’t “OK”, the method throws an exception.
Getting the Latitude and Longitude of a Location
Finally, in Program.cs
, we can get our coordinates:
var services = new ServiceCollection(); services.AddHttpClient("MapsClient", client => { client.BaseAddress = new Uri("https://maps.googleapis.com/"); }); services.AddSingleton<LatLongWithHttpClient>(provider => new LatLongWithHttpClient(provider.GetRequiredService<IHttpClientFactory>(), apiKey)); var serviceProvider = services.BuildServiceProvider(); var latLongWithHttpClient = serviceProvider.GetRequiredService<LatLongWithHttpClient>(); var coordinatesHttpClient = await latLongWithHttpClient.GetLatLongFromAddressAsync(address); Console.WriteLine($"Address ({address}) is at {coordinatesHttpClient.Latitude}, {coordinatesHttpClient.Longitude}");
Here, we initialize a new ServiceCollection
, which serves as a container for adding and configuring services. It’s where we register dependencies that our application will use.
Then, we register a named instance of HttpClient
configured with a base address set to Google’s Geocoding API URL.
Next, we add LatLongWithHttpClient
as a singleton service. This means that one instance of LatLongWithHttpClient
will be created and used throughout the app’s lifetime. This instance is created using IHttpClientFactory
and an apiKey
, both fetched from the service collection. IHttpClientFactory
is injected to help with the creation of HttpClient
instances that are pre-configured with the settings defined earlier (like the base address). This simplifies how our app handles resources and keeps HTTP request settings consistent across the board.
After that, BuildServiceProvider
turns the service collection into a service provider that can create and supply the registered services whenever needed.
Once that is set up, we use GetRequiredService<LatLongWithHttpClient>()
to fetch the LatLongWithHttpClient
instance from the service container. If it’s unavailable, it will throw an exception.
Lastly, we use the latLongWithHttpClient
instance to call GetLatLongFromAddressAsync()
with an address. This asynchronous method gets the latitude and longitude of the address and assigns the result to coordinatesHttpClient
. The latitude and longitude values are stored as properties of this object, specifically coordinatesHttpClient.Latitude
and coordinatesHttpClient.Longitude
.
After we run the code, we should see this in the console:
Address (Miami, Florida) is at 25.7616798, -80.1917902
Conclusion
In this article, we covered two methods to find the latitude and longitude of a location in C#. We demonstrated using HttpClient through IHttpClientFactory for direct API calls, and using the GoogleMaps.LocationServices NuGet package.
Both methods interact with the Google Maps Geocoding API but offer different levels of control and simplicity.