Line of Sight API | Radio path and terrain profile between two points

Access & limitsPublic, no key needed · Medium▾

Open to everyone. A free API key from your profile raises the limits: send it in an X-API-Key header, or as api_key= in the address.

5 minutesHourDay
Without a key1060300
With a key302001,500

Log in to create a free key on your profile page.

Every answer carries X-RateLimit-Remaining and X-RateLimit-Reset. Over a limit, the answer is error 429 with retry_after in seconds. Refused requests do not count.

No key, no account Real terrain, worldwide A few milliseconds per path Routes capped at 500 km

Works out whether two points can see each other over the real shape of the ground between them. It walks the great circle from A to B in steps of about one terrain cell, reads the ground height at every step from our elevation store, adds the curvature of the earth, and compares the result with the straight line between the two antennas.

It returns whether the path is clear, where it is blocked if it is not, how much of the first Fresnel zone is clear, and the height each end would need for the line to get through. The ground heights alone are available from the terrain elevation API, and there is a map and chart version of this endpoint in the line of sight tool.

GET https://www.altimetercloud.com/api/los/ from to

Parameters

NameDefaultWhat it does
fromrequiredPoint A as lat,lon in decimal degrees. lat1= and lon1= work too.
torequiredPoint B as lat,lon. lat2= and lon2= work too.
h11.5Height of the antenna at A above the ground, in metres.
h21.5Height at B above the ground. Use this for a rocket in the air.
freq868Frequency in MHz, used for the Fresnel zone only.
moderadioradio uses 4/3 of the earth radius for atmospheric refraction. optical uses the true radius.
stepcell sizeSample spacing in metres. Left alone it follows the terrain tier, between 30 and 120 m.
profile1Set to 0 to leave out the per sample arrays and get only the verdict.
corridor0Set to 1 to add a block of ground either side of the route for 3D use.
along, across280, 170Corridor sample counts along the route and across it. Caps are 800 and 400.
width0.8Corridor width as a fraction of the route length.
over0.15How far the corridor runs past each end, as a fraction of the route length, so the two points sit inside the block rather than on its edges.

Example

A valley floor in the Lake District looking at Scafell Pike, 16.7 km away on a bearing of 301 degrees, with a handheld at each end:

curl "https://www.altimetercloud.com/api/los/?from=54.3760,-2.9910&to=54.4542,-3.2116&h1=1.5&h2=1.5&profile=0"
Response, fields abridged
{
  "success": true,
  "distance_m": 16712.4,
  "distance_km": 16.712,
  "bearing_deg": 301.2,
  "step_m": 30.9,
  "samples": 542,
  "mode": "radio",
  "k_factor": 1.3333,
  "freq_mhz": 868,
  "a": { "lat": 54.376, "lon": -2.991, "ground_m": 75.4, "height_m": 1.5,
         "antenna_m": 76.9, "tier": "ultra" },
  "b": { "lat": 54.4542, "lon": -3.2116, "ground_m": 970.9, "height_m": 1.5,
         "antenna_m": 972.4, "tier": "ultra" },
  "clear": false,
  "clear_fresnel": false,
  "marginal": false,
  "clearance_m": -84.2,
  "fresnel_pct": -220.4,
  "margin_m": 2,
  "worst":      { "distance_m": 6234.1, "lat": 54.4051, "lon": -3.0868,
                  "ground_m": 542.0, "sightline_m": 457.8, "clearance_m": -84.2,
                  "fresnel_r_m": 38.2 },
  "blocked_at": { "distance_m": 6234.1, "lat": 54.4051, "lon": -3.0868,
                  "ground_m": 542.0, "sightline_m": 457.8, "clearance_m": -84.2,
                  "fresnel_r_m": 38.2 },
  "need": { "a_m": 116.3, "b_m": 227.5, "a_fresnel_m": 168.9,
            "b_fresnel_m": 330.1, "silly_m": 5000 },
  "tiers": { "ultra": 542 },
  "datum": "EGM96 mean sea level",
  "units": "metres"
}
The numbers above show the shape of the reply. Run the playground below for live figures, since the store is updated from time to time.

What the fields mean

FieldMeaning
clearTrue when nothing between the two antennas rises above the straight line joining them, with the earth bulge added to the ground.
clear_fresnelTrue when 60 percent of the first Fresnel zone is also clear, which is what a radio link wants rather than a bare line.
marginalTrue when the line clears, but by less than margin_m. Treat it as blocked until you have stood there.
clearance_mHow far the line passes above the ground at the tightest point. Negative means blocked, by that much.
fresnel_pctHow much of the first Fresnel zone is clear at its tightest point, as a percentage. 60 or more is good, under 60 costs a few dB.
margin_mHow much a sharp ridge crest can sit above its cell mean at the coarsest tier on this route: 2 m at 1 arcsec, 5 m at 3, 10 m at 6, 25 m at 18.
blocked_atThe first sample that breaks the line, or null when the path is clear. worst is always present and is the tightest point either way.
need.b_mThe height above ground at B that would put the line clear of everything in between. need.a_m is the same for A. A figure above silly_m means the obstruction sits too close to the other end for any height to fix it.
need.b_fresnel_mThe height for 60 percent Fresnel clearance as well. On flat ground this is always large, because the ground close to a low antenna is inside the zone whatever you do.
profileFour arrays of count entries: distance_m along the path, ground_m elevation, curve_m the earth bulge to add to it, and fresnel_r_m the first zone radius there.

Drawing the profile

The curvature of the earth is carried by adding the bulge to the ground rather than bending the sight line, which is the usual path profile convention and means the sight line can be drawn as a straight line between a.antenna_m and b.antenna_m. So for each sample i:

terrain height to plot  = profile.ground_m[i] + profile.curve_m[i]
sea level to plot       = profile.curve_m[i]
sight line              = a.antenna_m + (b.antenna_m - a.antenna_m) * i / (count - 1)
Fresnel edges           = sight line +/- 0.6 * profile.fresnel_r_m[i]

The bulge is d1 * d2 / (2 * k * R) with R the mean earth radius and k the k_factor in the reply: 1 optically, 4/3 for radio.

Errors

Error
{ "success": false, "error": { "code": "413",
  "message": "routes are limited to 500 km and this one is 605 km" } }

412 covers bad or missing coordinates and two points in the same place. 413 is a route over 500 km. Line of sight much past 100 km is blocked by the earth itself whatever the ground does, so the cap is generous already.

Limits and honesty

  • Heights are bare ground. No trees, no buildings, no masts. A line that clears a ridge by 5 m does not clear the forestry on it.
  • Every cell holds the mean height of the ground inside it, so a crest reads lower than it is. That is what margin_m and marginal are for.
  • Elevations are EGM96 mean sea level, so they compare directly with a barometric altitude.
  • Sea and anywhere without data read zero.
  • This is geometry, not a link budget. It says nothing about transmit power, antenna patterns, receiver sensitivity or interference.
  • No rate limit, no key, no logging of who asked. Please keep batch use sensible.

Try it

Crediting AltimeterCloud

Free to use. If you use this API, or anything made from the answers, in a commercial product or service, credit AltimeterCloud with a link to www.altimetercloud.com at the point of use: on the screen, page or printout where the data appears, for example “Data from AltimeterCloud.com”. Any credits the data's own sources ask for, listed on this page, apply as well.

Terrain data credits

The ground heights come from the Terrain Tiles dataset (Mapzen / Tilezen, on AWS Open Data), reduced into resolution tiers by Altimeter Cloud. Wherever you show heights from this API, credit their sources. Each answer carries a short form of the list below in its credits field.

  • ArcticDEM terrain data DEM(s) were created from DigitalGlobe, Inc., imagery and funded under National Science Foundation awards 1043681, 1559691, and 1542736
  • Australia terrain data © Commonwealth of Australia (Geoscience Australia) 2017
  • Austria terrain data © offene Daten Österreichs – Digitales Geländemodell (DGM) Österreich
  • Canada terrain data contains information licensed under the Open Government Licence – Canada
  • Europe terrain data produced using Copernicus data and information funded by the European Union - EU-DEM layers
  • Global ETOPO1 terrain data U.S. National Oceanic and Atmospheric Administration
  • Mexico terrain data source: INEGI, Continental relief, 2016
  • New Zealand terrain data Copyright 2011 Crown copyright (c) Land Information New Zealand and the New Zealand Government (All rights reserved)
  • Norway terrain data © Kartverket
  • United Kingdom terrain data © Environment Agency copyright and/or database right 2015. All rights reserved
  • United States 3DEP (formerly NED) and global GMTED2010 and SRTM terrain data courtesy of the U.S. Geological Survey

The licences behind each statement are on the terrain tiles attribution page.