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 withnew ProviderFactory().SetApiKeySettings(new ApiKeySettings { ... })(namespacesTMS.Maps.Core.FactoriesandTMS.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 useusing 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.