Skip to main content
Table of Contents

Calling the services

The services are plain .NET classes that need no map and no UI. They work the same in WinForms, WPF, .NET MAUI and Blazor apps, and in console apps, ASP.NET Core and background workers. Each one takes a provider and that provider's API key in its constructor, and every call returns a ServiceResult<T>.

The services are included in the Trial package you installed in the Quick Start, so there is nothing extra to install.

Warning

Do not add TMS.Maps.Services or another individual TMS.Maps.* package next to a .Trial package. The Trial package already contains them, and the two conflict.

Before you call a service

  • Register your license key first. Call RegisterLicense(string) once at startup, as in the Quick Start. Without a valid license, the service constructor and every call throw TMSLicensingException.
  • Pass an API key. Every service provider needs one, including the providers whose map needs no key. With no key, the constructor throws InvalidOperationException. You can also set the keys once for the whole app with new ProviderFactory().SetApiKeySettings(new ApiKeySettings { ... }) (namespaces TMS.Maps.Core.Factories and TMS.Maps.Core.Settings) and then leave the key out of the constructor.
  • Pick a provider that supports the service. A provider without that service makes the constructor throw NotSupportedException. See Which provider supports what.
  • Dispose the service when you are done with it: the services are IDisposable, so use using var.

The result pattern

Every service call returns a ServiceResult<T>. Use IsError(out var data) to branch: it returns true when the call failed (or produced no payload) and hands you the payload otherwise.

using TMS.Maps.Core.Extensions;   // GetErrorMessages()

var result = await geocoding.GetAsync("Grote Markt, Antwerp");

if (result.IsError(out var matches))
{
    Console.WriteLine(result.GetErrorMessages());
    return;
}

// 'matches' is not null here, but it can be empty when nothing was found.
foreach (var m in matches)
    Console.WriteLine(m.FormattedAddress);

A successful call can return an empty list, so use FirstOrDefault() rather than First(). Errors reported by the provider (an invalid key, a quota, a bad request) come back in the result. Invalid arguments throw, and so do network failures and timeouts: wrap calls in try/catch when the network can fail.

Geocoding

Forward (address to coordinates) and reverse (coordinates to address) geocoding via GeocodingService. Both directions use GetAsync.

using TMS.Maps.Core.Models;
using TMS.Maps.Geocoding;

using var geocoding = new GeocodingService(Providers.Google, "YOUR_API_KEY");

// Forward geocoding
var forward = await geocoding.GetAsync("Grote Markt, Antwerp");
if (!forward.IsError(out var places) && places.FirstOrDefault() is { } first)
    Console.WriteLine($"{first.FormattedAddress}: {first.Coordinate.Latitude}, {first.Coordinate.Longitude}");

// Reverse geocoding
var reverse = await geocoding.GetAsync(new Coordinate(51.0543, 3.7174));   // Ghent
if (!reverse.IsError(out var addresses))
    Console.WriteLine(addresses.FirstOrDefault()?.FormattedAddress);

For Google-specific fields, GoogleGeocodingService returns GoogleGeocode results. It always uses Google, so it takes no provider. Pass the key by name, because the first constructor parameter is an HttpClient:

using var google = new GoogleGeocodingService(apiKey: "YOUR_API_KEY");
var typed = await google.GetAsync("Ghent");

Directions

Calculate routes between two coordinates, or from a full DirectionsRequest, with DirectionsService. Origin and destination are coordinates: geocode an address first.

using TMS.Maps.Core.Models;
using TMS.Maps.Core.Models.Services;
using TMS.Maps.Directions;

using var directions = new DirectionsService(Providers.Google, "YOUR_API_KEY");

var request = new DirectionsRequest(
    new Coordinate(51.2194, 4.4025),   // Antwerp
    new Coordinate(51.0543, 3.7174),   // Ghent
    TravelMode: TravelMode.Driving,
    AvoidTolls: true);

var result = await directions.GetAsync(request);
if (!result.IsError(out var routes) && routes.FirstOrDefault() is { } route)
{
    Console.WriteLine($"{route.Distance / 1000:F1} km, {TimeSpan.FromSeconds(route.Duration)}");
    foreach (var leg in route.Legs)
        Console.WriteLine($"{leg.Distance} m, {leg.Duration} s");
}

Distances are in metres and durations in seconds.

Elevation

Resolve elevation for one or many coordinates via ElevationService. Google Maps is the provider that supports elevation in this release.

using TMS.Maps.Core.Models;
using TMS.Maps.Elevation;

using var elevation = new ElevationService(Providers.Google, "YOUR_API_KEY");

var result = await elevation.GetAsync(new[]
{
    new Coordinate(51.0543, 3.7174),   // Ghent
    new Coordinate(50.8503, 4.3517),   // Brussels
});
if (!result.IsError(out var points))
    foreach (var p in points)
        Console.WriteLine($"{p.Coordinate}: {p.Elevation:F0} m");

Places

Search for points of interest with PlacesService, passing a PlacesRequest. The default request type is AutoComplete; the other types depend on the provider (see the table below).

using TMS.Maps.Core.Models;
using TMS.Maps.Core.Models.Services;
using TMS.Maps.Places;

using var places = new PlacesService(Providers.Google, "YOUR_API_KEY");

var request = new PlacesRequest(
    Query: "coffee",
    RequestType: PlacesRequestType.TextSearch,
    Location: new Coordinate(51.0543, 3.7174),
    Radius: 1500);

var result = await places.GetAsync(request);
if (!result.IsError(out var hits))
    foreach (var place in hits)
        Console.WriteLine($"{place.Name}, {place.Address}");

Static maps

Build a static map image URL, or download the image, with StaticMapService.

using TMS.Maps.Core.Models;
using TMS.Maps.Core.Models.Services;
using TMS.Maps.StaticMap;

using var staticMap = new StaticMapService(Providers.Google, "YOUR_API_KEY");

// Just need a URL? GetUrl makes no request.
string url = staticMap.GetUrl(new Coordinate(51.0543, 3.7174));

// Or download the rendered image: GetAsync returns the image as a Stream.
var request = new StaticMapRequest(new Coordinate(51.0543, 3.7174), Width: 800, Height: 600, Zoom: 14);
var result = await staticMap.GetAsync(request);
if (!result.IsError(out var imageStream))
{
    await using var file = File.Create("ghent.png");
    await imageStream.CopyToAsync(file);
}
Warning

The URL from GetUrl contains your API key. Do not hand it to browsers or other untrusted clients.

Location

Resolve the caller's location, or the location of an IP address, with LocationService. The result is a single location, not a list.

using TMS.Maps.Core.Models;
using TMS.Maps.Location;

using var location = new LocationService(Providers.IPStack, "YOUR_API_KEY");

// The caller's own location
var result = await location.GetAsync();

// The location of a specific IP address (IPStack only)
var byIp = await location.GetByIpAsync("8.8.8.8");

if (!byIp.IsError(out var loc))
    Console.WriteLine($"{loc.City}, {loc.Country}: {loc.Coordinate.Latitude}, {loc.Coordinate.Longitude}");

Only IPStack looks up the IP address you pass. Google's Geolocation API ignores it and returns the caller's location, with only the coordinate and accuracy filled in. IPStack's free plan may need LocationConnection.Http.

Which provider supports what

Service Providers
GeocodingService Google Maps, Azure Maps, HERE, Apple MapKit, GeoApify
DirectionsService Google Maps, Azure Maps, HERE, Apple MapKit, Mapbox, TomTom, GeoApify, OpenRouteService
ElevationService Google Maps
PlacesService Google Maps, Azure Maps, HERE, Apple MapKit, TomTom, GeoApify
StaticMapService Google Maps, Azure Maps, HERE, Mapbox, TomTom
LocationService Google Maps, IPStack

Places request types per provider (other types throw NotSupportedException):

Provider AutoComplete TextSearch NearbySearch PlaceDetail
Google Maps Yes Yes Yes Yes
Azure Maps Yes Yes
HERE Yes Yes
Apple MapKit Yes Yes Yes Yes
TomTom Yes
GeoApify Yes

Capability flags

ProviderExtensions filters the Providers enum by its ProviderCapabilities flags:

using TMS.Maps.Core.Attributes;
using TMS.Maps.Core.Extensions;
using TMS.Maps.Core.Models;

foreach (var p in ProviderExtensions.GetProvidersByCapability(ProviderCapabilities.Directions))
    Console.WriteLine(p);

bool flagged = Providers.Google.SupportsElevation();
Note

These helpers read the flags only. A few flags are not backed by a service implementation in this release, so the constructor throws NotSupportedException for them: geocoding on Mapbox and OpenRouteService, and elevation on OpenRouteService. Azure Maps has an elevation implementation but no Elevation flag, so it is not available either. Use the tables above to choose a provider.

See also the API reference for the full set of overloads and result types.