Find the address from a GPS position

Look up the nearest address from longitude and latitude with reverse geocoding, and handle GPS inaccuracy with a radius search.

4 min Level: Intermediate ✓ Code tested

A tradesperson app should fill in the address from the phone's position. An insurance claim only has a point on a map. A fleet tool should show which address a vehicle is parked at. All three are reverse geocoding: from coordinate to address.

The API has two endpoints for it, plus a radius search that gives you more control when the position is inaccurate.

The simple call

GET /adgangsadresser/reverse returns the nearest active access address (the building entrance) to the point:

Shell#reverse-curl
curl -s "https://api.danadresse.dk/adgangsadresser/reverse?x=12.5757&y=55.7326" \
  -H "X-Api-Key: $DANADRESSE_API_KEY"

x is longitude and y is latitude. Swapping them is the most common mistake: Denmark lies at roughly 8-15° east and 54-58° north, so an x above 50 is almost always a swap.

The response has the same shape as GET /adgangsadresser/{id}, and struktur=mini or struktur=flad works here too. The most important fields:

FieldContent
idThe access-address UUID
adressebetegnelseE.g. Strandvejen 100, 2900 Hellerup
vejstykke.navn, husnr, postnummer.nr, postnummer.navnThe parts of the address
kommune, sogn, politikreds …Administrative areas
adgangspunkt.koordinaterThe address's own point, [x, y]

If your coordinates are in UTM32 (metres), add srid=25832. Then both the input and the coordinates in the response are in metres:

Shell#reverse-utm
curl -s "https://api.danadresse.dk/adgangsadresser/reverse?x=724434.93&y=6175755.61&srid=25832" \
  -H "X-Api-Key: $DANADRESSE_API_KEY"

GET /adresser/reverse works the same way but returns a full address with floor and door. It picks one of the units in the building. In a block of flats the API cannot know which flat the user is in, so let the user choose floor and door afterwards. Fetch the list with GET /adresser?adgangsadresseid={id}&struktur=mini.

What reverse does not tell you

  • No distance. The response does not say how far the point is from the address. Calculate it yourself from adgangspunkt.koordinater (see Distance between two addresses).
  • No maximum distance. The endpoint looks up to about 60 km away. If the user stands in the middle of a field, you still get an address, maybe a few kilometres away. Only when there is no address within that distance does the API answer 404 with the code not_found.
  • Active addresses only. Current and provisional addresses are included. Retired addresses are not.
  • Always the nearest. Reverse searches in growing areas until the nearest entrance is found, also in dense city areas. It returns the same address the radius search below puts first.

GPS is not exact: use a radius

A phone reports its position with an uncertainty (coords.accuracy in the browser, in metres). Outdoors it is typically 5-20 metres, indoors and between tall buildings often 50 metres or more. With an uncertainty of 40 metres the user could just as well be at the house next door.

So use cirkel=lon,lat,radius on /adgangsadresser with the radius set from the uncertainty. Results come sorted with the nearest first. If there are several candidates, show them as a short list:

JavaScript#gps-node
const API = "https://api.danadresse.dk";
const headers = { "X-Api-Key": process.env.DANADRESSE_API_KEY };

function afstandMeter(lat1, lon1, lat2, lon2) {
  const R = 6371008.8, rad = Math.PI / 180;
  const dLat = (lat2 - lat1) * rad, dLon = (lon2 - lon1) * rad;
  const a = Math.sin(dLat / 2) ** 2 +
    Math.cos(lat1 * rad) * Math.cos(lat2 * rad) * Math.sin(dLon / 2) ** 2;
  return 2 * R * Math.asin(Math.sqrt(a));
}

async function adresserVed(lat, lon, noejagtighed) {
  const radius = Math.min(Math.max(Math.round(noejagtighed), 25), 250);
  const url = new URL(`${API}/adgangsadresser`);
  url.searchParams.set("cirkel", `${lon},${lat},${radius}`);
  url.searchParams.set("struktur", "mini");
  url.searchParams.set("per_side", "5");
  const res = await fetch(url, { headers });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const kandidater = await res.json();
  if (kandidater.length > 0) return kandidater;
  // Nothing within the radius: fall back to the nearest address at all
  const rev = await fetch(`${API}/adgangsadresser/reverse?x=${lon}&y=${lat}`, { headers });
  if (rev.status === 404) return [];
  const a = await rev.json();
  const [x, y] = a.adgangspunkt.koordinater;
  return [{ id: a.id, betegnelse: a.adressebetegnelse, x, y }];
}

const lat = 55.67565, lon = 12.56935, noejagtighed = 30;
for (const a of await adresserVed(lat, lon, noejagtighed)) {
  console.log(`${a.betegnelse} (afstand ${Math.round(afstandMeter(lat, lon, a.y, a.x))} m)`);
}

The radius must be between 1 and 50,000 metres, otherwise the API answers 400 with the code invalid_cirkel. With srid=25832 the centre is in metres.

In the browser

navigator.geolocation gives position and uncertainty. Call the API with a browser key locked to your domain (see Browser keys and allowed domains):

JavaScript#gps-browser
const API_KEY = "dawa_live_your_browser_key"; // locked to your domain

navigator.geolocation.getCurrentPosition(async ({ coords }) => {
  const radius = Math.min(Math.max(Math.round(coords.accuracy), 25), 250);
  const url = `https://api.danadresse.dk/adgangsadresser?cirkel=${coords.longitude},${coords.latitude},${radius}&struktur=mini&per_side=5`;
  const res = await fetch(url, { headers: { "X-Api-Key": API_KEY } });
  const forslag = await res.json();
  visForslag(forslag.map((a) => ({ id: a.id, tekst: a.betegnelse })));
}, (fejl) => console.warn(fejl.message), { enableHighAccuracy: true, timeout: 10000 });

The browser asks the user for permission. If the user says no, or it takes too long, fall back to a normal address field with autocomplete.

Edge cases

  • At sea or abroad. If the point is far from any Danish address, reverse gives 404 and the circle gives an empty list.
  • Large plots. The address point sits at the entrance or the road. On a farm or an industrial site the user can be several hundred metres from the point and still be at the right address.
  • Missing parameters. Both x and y are required. If one is missing the API answers 400 with invalid_query_parameter.
  • Precision in the response. Coordinates in the response have eight decimals, as in DAWA. To calculate distances in metres without converting, use srid=25832. Any srid other than 4326 and 25832 gives 400.

Next steps

Related guides

Markdown source Spotted an error? Tell us API-reference (Swagger)

Try it with a free key

2,000 calls/month on the free plan. No credit card.

Create account →