Skip to content
Shiny.NET

Reverse Geocoding

Downloads NuGet downloads for Shiny.Gps
Operating Systems
Android
iOS
Windows
macOS
Linux
Web

Reverse geocoding turns a Position into a readable address: street, city, region, postal code and country. Shiny uses the geocoder built into iOS, Mac Catalyst and Android, and the free OpenStreetMap Nominatim API everywhere else, so there is no API key to manage on any platform. Every geocoder needs network access.

Platform Geocoder
iOS / Mac Catalyst 26+ MapKit MKReverseGeocodingRequest
iOS / Mac Catalyst 15-25 CoreLocation CLGeocoder
Android 33+ android.location.Geocoder (listener API)
Android < 33 android.location.Geocoder (run off the calling thread)
Android without a geocoding backend (no Google Play Services) OpenStreetMap Nominatim
Windows, Linux, macOS, Blazor WebAssembly OpenStreetMap Nominatim
using Shiny;
services.AddGeocoding();

Reverse geocoding doesn’t need a location permission, because you pass it the position. Getting that position from the GPS still does.

If you register your own IGeocoder before calling AddGeocoding(), Shiny keeps yours.

Inject IGeocoder:

public class AddressService(IGeocoder geocoder, IGpsManager gps)
{
public async Task<string?> GetCurrentAddress(CancellationToken ct)
{
if (!geocoder.IsSupported)
return null;
var reading = await gps.GetLastReadingOrCurrentPosition(cancellationToken: ct);
if (reading == null)
return null;
var placemarks = await geocoder.ReverseGeocode(reading.Position, ct);
return placemarks.FirstOrDefault()?.FormattedAddress;
}
}

ReverseGeocode returns the matches, most relevant first, and an empty list when nothing matched.

On Windows, Linux, macOS, Blazor WebAssembly and Android devices without Play Services, IGeocoder calls Nominatim’s reverse API. The implementation is internal: inject IGeocoder as usual, and configure it through AddGeocoding:

services.AddGeocoding(o =>
{
o.Email = "support@myapp.com"; // lets the server's operators reach you before they block you
o.Language = "en"; // defaults to the current UI culture
// o.BaseUri = new Uri("https://nominatim.mycompany.com/"); // your own server, for heavy use
});
Option Default Description
BaseUri NominatimOptions.PublicServer (https://nominatim.openstreetmap.org/) The Nominatim server. Point it at a self-hosted or other Nominatim-compatible server to get past the public rate limit
UserAgent <entry assembly>/<version> (Shiny.Gps) Identifies your app, as the public server requires. Browsers don’t let a page set it, so Blazor WebAssembly doesn’t send it and the browser’s Referer identifies the app instead
Email null A contact address sent with each request. The public server asks for one if you send a lot of requests
Language current UI culture The language results come back in (en, fr-CA). null lets the server use local names
MinimumRequestInterval 1 second The minimum time between requests. Lower it only for a server you run yourself
CacheSize 100 How many results are kept in memory, keyed on the position rounded to about a metre. 0 turns the cache off

The public server is free, but its usage policy limits clients to one request a second, requires an identifying User-Agent, and asks clients to cache results. Shiny keeps to it: requests run one at a time and are spaced by MinimumRequestInterval, so calls made close together queue up. A failed request still counts toward the limit. Nominatim returns one placemark at most. Don’t reverse geocode every GPS reading against the public server. Geocode when the user asks, or after a significant move.

Property Description
Position The position the placemark describes
Name Name of the place: a landmark or business, or the street address
SubThoroughfare Street number
Thoroughfare Street name
SubLocality Neighbourhood or district
Locality City or town
SubAdministrativeArea County or equivalent
AdministrativeArea State, province or equivalent
PostalCode Postal or zip code
CountryCode ISO 3166-1 alpha-2 country code
CountryName Localized country name
FormattedAddress The full address on one line, formatted by the platform

A component the geocoder couldn’t resolve is null. Results come back in the device’s language (with Nominatim, NominatimOptions.Language, which defaults to it). With Nominatim, CountryCode is upper-cased to match the native geocoders, and Position is the point Nominatim matched rather than the one you asked about.

claude plugin marketplace add shinyorg/skills
claude plugin install shiny@shiny

One plugin installs all 48 Shiny skills. Your agent loads only the skill relevant to what you're building, so there's no cost to having them all available.

copilot plugin marketplace add https://github.com/shinyorg/skills
copilot plugin install shiny@shiny

One plugin installs all 48 Shiny skills. Your agent loads only the skill relevant to what you're building, so there's no cost to having them all available.

View shiny-locations Skill