Leaflet.Geodesic
August 29, 2025 · View on GitHub
Add-on for Leaflet to draw geodesic lines and circles. A geodesic line is the shortest path between two given positions on the earth surface. It's based on Vincenty's formulae implemented by Chris Veness for highest precision.
⚗️ Leaflet 2.0 Preview
Leaflet is planning to release a new major version by the end of this year. Leaflet.Geodesic already supports this with a new release: v3.0.0-alpha.2.
Migration Guide
Migrate from Leaflet.Geodesic 2.x to 3.x
Leaflet.Geodesic requires Leaflet 2 to work.
- Replace the
<script src=...for Leaflet and Leaflet.Geodesic with animportmapas follows:
<script type="importmap">
{
"imports": {
"leaflet": "https://unpkg.com/leaflet@2.0.0-alpha.1/dist/leaflet.js",
"leaflet.geodesic": "https://unpkg.com/leaflet.geodesic@3.0.0-alpha.2/dist/leaflet.geodesic.esm.min.js"
},
"integrity": {
"https://unpkg.com/leaflet.geodesic@3.0.0-alpha.2/dist/leaflet.geodesic.esm.min.js": "sha512-5lUYcVPLuTl2uEsodxmC2AJQ/uFqQ2mEWPtHGL8ktTr5f212ddNqR1iGyuT0Gy8HY8V4KZmVX6PsEOuFBHZD3Q=="
}
}
</script>
The
integrityproperty is optional but recommended (Subresource Integrity).
- Replace the
<script type="text/javascript">of your map setup with
<script type="module">
import { LeafletMap, TileLayer, Marker, LatLng, Polyline, Control, DomUtil } from 'leaflet';
import { GeodesicLine } from "leaflet.geodesic";
// ...
</script>
Leaflet.Geodsic provides two classes:
GeodesicLineandGeodesicCircle. Make sure to import the class you need.
- Classes provided by Leaflet and Leaflet.Geodesic must be instanciated using
newas follows:
// Leaflet setup
const map = new LeafletMap(/* your arguments */)
const tileLayer = new TileLayer(/* your arguments */).addTo(map);
// Leaflet.Geodesic setup
const geodesicLine = new GeodesicLine().addTo(map);
const geodesicCircle = new GeodesicCircle().addTo(map);
Please note that the global
Lobject is no longer used.
Everything else is unchanged and the existing documentation is still valid.
See Leaflet.Geodesic 3 Demos on how to use Leaflet.Geodesic 3 with Leaflet 2.
Add the plugin to your project
Leaflet.Geodesic is available via CDN. Add the following snippet to your html-file after you have included leaflet.js.
<!-- Make sure you put this AFTER leaflet.js -->
<script src="https://cdn.jsdelivr.net/npm/leaflet.geodesic"></script>
Leaflet.Geodesic is available via the following CDNs:
Add it in your nodejs-project with npm i leaflet.geodesic.
It is good practice, to pin the plug-in to a specific version and use Subresource Integrity. Check the release page for the latest version, links and checksum. A checksum can by verified with npm run build, is stored in dist/leaflet.geodesic.umd.min.js.sha512 on jsDelivr and unpkg and is shown in the release notes.
Basic usage
L.Geodesicdraws geodesic lines between all points of a given line- or multiline-string.L.GeodesicCircledraws a circle with a specific radius around a given point.
The Objects can be created as follows:
const geodesicLine = new L.Geodesic().addTo(map); // creates a blank geodesic-line-object and adds it to the map
const geodesicCircle = new L.GeodesicCircle().addTo(map); // creates a blank geodesic-circle-object and adds it to the map
Alternative method:
const geodesicLine = L.geodesic().addTo(map); // lower-case, w/o new-keyword
const geodesicCircle = L.geodesiccircle().addTo(map); // lower-case, w/o new-keyword
Make sure you add the geodesic-object to the map (.addTo(map)). It won't display otherwise.
Each constructor is defined as:
Geodesic(latlngs?: L.LatLngExpression[] | L.LatLngExpression[][], options?: GeodesicOptions)
GeodesicCircle(center?: L.LatLngExpression, options?: GeodesicOptions)
Both classes are extended from L.Polyline, so all methods, events and options for L.Polyline can be used with L.Geodesic and L.GeodesicCircle here as well. Any alt-properties given with any points are preserved by L.Geodesic.
Geodesic Lines
This draws a line. The geometry (points) to use can be given during creation as:
Objects (Literals)
const Berlin = {lat: 52.5, lng: 13.35};
const LosAngeles = {lat: 33.82, lng: -118.38};
const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
LatLng-Class
const Berlin = new L.LatLng(52.5, 13.35);
const LosAngeles = new L.LatLng(33.82, -118.38);
const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
Tuples
const Berlin = [52.5, 13.35];
const LosAngeles = [33.82, -118.38];
const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);

Line-strings
Multiple consecutive points can be given as an array (linestring):
const places = [
new L.LatLng(52.5, 13.35), // Berlin
new L.LatLng(33.82, -118.38), // Los Angeles
new L.LatLng(-33.44, -70.71), // Santiago
new L.LatLng(-33.94, 18.39), // Capetown
];
const geodesic = new L.Geodesic(places).addTo(map);

Multi-line-strings
Multiple independent linestrings can be defined as a 2-dimensional array of points:
const places = [
[ // 1st line
new L.LatLng(52.5, 13.35), // Berlin
new L.LatLng(33.82, -118.38), // Los Angeles
],
[ // 2nd line
new L.LatLng(-33.44, -70.71), // Santiago
new L.LatLng(-33.94, 18.39), // Capetown
]
];
const geodesic = new L.Geodesic(places).addTo(map);

GeoJSON-Support
GeoJSON-data can be used to create geodesic lines with the fromGeoJson() method:
const geojson = {
"type": "LineString",
"coordinates": [
[13.35, 52.5], [-122.33, 47.56], [18.39, -33.94], [116.39, 39.92], [13.35, 52.5]
]
};
const geodesic = new L.Geodesic().addTo(map);
geodesic.fromGeoJson(geojson);

Updating the geometry
Set new geometry
The Geodesic-Class provides a setLatLngs()-Method, that can be used to update the geometry of an existing L.Geodesic-object:
const geodesic = new L.Geodesic().addTo(map); // add empty object to the map
const Berlin = new L.LatLng(52.5, 13.35);
const LosAngeles = new L.LatLng(33.82, -118.38);
geodesic.setLatLngs([Berlin, LosAngeles]) // update in-place
The setLatLngs()-Method accepts the same types (Literal, Tuple, LatLang-Class, Linstring, Multilinestring) as the L.Geodesic-constructor itself. Please refer to the section about geodesic circles below, on how to update a circle geometry.
Delete geometry
Delete the existing geometry by setting an empty array geodesic.setLatLngs([]).
adding points
Points can be added to existing geodesic lines with addLatLng():
const Berlin = new L.LatLng(52.5, 13.35);
const LosAngeles = new L.LatLng(33.82, -118.38);
const Beijing = new L.LatLng(39.92, 116.39);
const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
geodesic.addLatLng(Beijing); // results in [[Berlin, LosAngeles, Beijing]
The new point will always be added to the last linestring of a multiline. You can define a specific linestring to add to by reading the points property before and hand over a specific linestring as second parameter:
const Berlin = new L.LatLng(52.5, 13.35);
const LosAngeles = new L.LatLng(33.82, -118.38);
const Beijing = new L.LatLng(39.92, 116.39 );
const Capetown = new L.LatLng(-33.94, 18.39 );
const Santiago = new L.LatLng(-33.44, -70.71);
const geodesic = new L.Geodesic([[Berlin, LosAngeles], [Santiago, Capetown]]).addTo(map);
geodesic.addLatLng(Beijing, geodesic.points[0]); // results in [[Berlin, LosAngeles, Beijing], [Santiago, Capetown]]
Drawing over the antimeridian
In some cases it is required to draw over the antimeridian (dateline) to show a continuous path. This is possible by setting the wrap-option to false. Leaflet.Geodesic will make sure to shift the individual points to draw a continuous line, even if the coordinates are not properly aligned to a map section. See interactive example
const Berlin = new L.LatLng(52.5, 13.35);
const LosAngeles = new L.LatLng(33.82, -118.38);
const Capetown = new L.LatLng(-33.94, 18.39 );
const Santiago = new L.LatLng(-33.44, -70.71);
const Tokyo = new L.LatLng(35.47, 139.15 + 360); // these points are in another map section
const Sydney = new L.LatLng(-33.91, 151.08 + 10 * 360); // but will get shifted accordingly
const geodesic = L.geodesic(
[ Santiago, Tokyo, Capetown, Sydney, LosAngeles, Berlin],
{ wrap: false
}).addTo(map);

Line Options
All options defined for Polyline and Path for can be used Leaflet.Geodesic.
The most important options are:
| Option | Type | Default | Description |
|---|---|---|---|
color | String | "#3388ff" | Stroke color |
weight | Number | 3 | Stroke width in pixels |
opacity | Number | 1.0 | Stroke opacity (0=transparent, 1=opaque) |
steps | Number | 3 | Level of detail (vertices = 1+2**(steps+1)) for the geodesic line. More steps result in a smoother line. Range: 0..8 |
wrap | Boolean | true | Wrap geodesic line at antimeridian. Set to false, to draw a line over the antimeridian. See no-wrap demo for example. |
Example:
const Berlin = new L.LatLng(52.5, 13.35);
const LosAngeles = new L.LatLng(33.82, -118.38);
const options = {
weight: 20,
opacity: 0.5,
color: 'red',
};
const geodesic = new L.Geodesic([Berlin, LosAngeles], options).addTo(map);

Geodesic Circles
Circles can be added with another class called L.GeodesicCircle as follows:
const Seattle = new L.LatLng(47.56, -122.33);
const geodesiccircle = new L.GeodesicCircle(Seattle, {
radius: 3000*1000, // 3000km in meters
}).addTo(map);

The geometry of a circle can be updated with the following methods:
setLatLng(latlng: L.LatLngExpression)- set a new centersetRadius(radius: number)- update the radius
Handling of filled circles crossing the antimeridian (wrapping) and very large circles near the poles are not yet supported. Set fill: false in these cases to avoid display artefacts.
Circle Options
| Option | Type | Default | Description |
|---|---|---|---|
radius | Number | 1000*1000 | Radius in meters |
steps | Number | 24 | Number of segments that are used to approximate the circle. |
fill | boolean | true | Draws a filled circle. |
color | String | "#3388ff" | Stroke color |
weight | Number | 3 | Stroke width in pixels |
opacity | Number | 1.0 | Stroke opacity (0=transparent, 1=opaque) |
Please refer to the options for Polyline and Path for additional settings.
Statistics
The L.Geodesic and L.GeodesicCircle-class provide a statistics-Object with the following properties:
| Property | Type | Description |
|---|---|---|
totalDistance | Number | The total distance of all geodesic lines in meters. (circumference for L.GeodesicCircle) |
distanceArray | Number[] | The distance for each separate linestring in meters |
points | Number | Number of points that were given on creation or with setLatLngs() |
vertices | Number | Number of vertices of all geodesic lines that were calculated |
Distance Calculation
The L.Geodesic provides a distance-function to calculate the precise distance between two points:
const Berlin = new L.LatLng(52.5, 13.35);
const Beijing = new L.LatLng(39.92, 116.39);
const line = new L.Geodesic();
const distance = line.distance(Berlin, Beijing);
console.log(`${Math.floor(distance/1000)} km`) // prints: 7379 km
The L.GeodesicCircle-class provides a distanceTo-function to calculate the distance between the current center and any given point:
const Berlin = new L.LatLng(52.5, 13.35);
const Beijing = new L.LatLng(39.92, 116.39);
const circle = new L.GeodesicCircle(Berlin);
const distance = circle.distanceTo(Beijing);
console.log(`${Math.floor(distance/1000)} km`) // prints: 7379 km
Scientific background
All calculations are based on the WGS84-Ellipsoid (EPSG:4326) using Vincenty's formulae. This method leads to very precise calculations but may fail for some corner-cases (e.g. Antipodes). I use some workarounds to mitigate these convergence errors. This may lead to reduced precision (a.k.a. slightly wrong results) in these cases. This is good enough for a web mapping application but you shouldn't plan a space mission based on this data. OMG, this section has just become a disclaimer...
