UNPKG

12.1 kBMarkdownView Raw
1# Leaflet.Geodesic
2[![Build Status](https://travis-ci.org/henrythasler/Leaflet.Geodesic.svg?branch=master)](https://travis-ci.org/henrythasler/Leaflet.Geodesic) [![npm](https://img.shields.io/npm/v/leaflet.geodesic)](https://www.npmjs.com/package/leaflet.geodesic) [![Coverage Status](https://coveralls.io/repos/github/henrythasler/Leaflet.Geodesic/badge.svg?branch=master)](https://coveralls.io/github/henrythasler/Leaflet.Geodesic?branch=master) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=henrythasler_Leaflet.Geodesic&metric=alert_status)](https://sonarcloud.io/dashboard?id=henrythasler_Leaflet.Geodesic)
3
4Add-on for [Leaflet](http://leafletjs.com/) to draw [geodesic](http://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid) 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](https://en.wikipedia.org/wiki/Vincenty%27s_formulae) implemented by [Chris Veness](https://github.com/chrisveness/geodesy) for highest precision.
5
6[![demo](docs/img/demo.png)](https://blog.cyclemap.link/Leaflet.Geodesic/basic-interactive.html)
7
8[Live Demos and Tutorials](https://blog.cyclemap.link/Leaflet.Geodesic/)
9
10[Observable-Notebook](https://observablehq.com/@henrythasler/leaflet-geodesic)
11
12[API-Documentation](https://blog.cyclemap.link/Leaflet.Geodesic/api)
13
14## Add the plugin to your project
15
16Leaflet.Geodesic is available via CDN. Add the following snippet to your html-file after you have [included leaflet.js](https://leafletjs.com/examples/quick-start/).
17
18```html
19<!-- Make sure you put this AFTER leaflet.js -->
20<script src="https://cdn.jsdelivr.net/npm/leaflet.geodesic"></script>
21```
22
23Leaflet.Geodesic is available from [unpkg](https://unpkg.com/browse/leaflet.geodesic/), [jsDelivr](https://www.jsdelivr.com/package/npm/leaflet.geodesic) and [npmjs](https://www.npmjs.com/package/leaflet.geodesic).
24
25Add it in your nodejs-project with `npm i leaflet.geodesic`.
26
27If possible, pin the plug-in to a specific version and use [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). Check the [release page](https://github.com/henrythasler/Leaflet.Geodesic/releases) 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.sha256` on [jsDelivr](https://www.jsdelivr.com/package/npm/leaflet.geodesic?path=dist) and [unpkg](https://unpkg.com/browse/leaflet.geodesic@2.5.1/dist/leaflet.geodesic.umd.min.js.sha256) and is shown in the [build-log](https://travis-ci.org/henrythasler/Leaflet.Geodesic/builds) for a tagged version.
28
29## Basic usage
30
31- `L.Geodesic` draws geodesic lines between all points of a given line- or multiline-string.
32- `L.GeodesicCircle` draws a circle with a specific radius around a given point.
33
34The Objects can be created as follows:
35
36```JavaScript
37const geodesicLine = new L.Geodesic().addTo(map); // creates a blank geodesic-line-object and adds it to the map
38const geodesicCircle = new L.GeodesicCircle().addTo(map); // creates a blank geodesic-circle-object and adds it to the map
39```
40
41Alternative method:
42
43```JavaScript
44const geodesicLine = L.geodesic().addTo(map); // lower-case, w/o new-keyword
45const geodesicCircle = L.geodesiccircle().addTo(map); // lower-case, w/o new-keyword
46```
47
48Make sure you add the geodesic-object to the map. It won't display otherwise.
49
50Each constructor is defined as:
51```JavaScript
52Geodesic(latlngs?: L.LatLngExpression[] | L.LatLngExpression[][], options?: GeodesicOptions)
53GeodesicCircle(center?: L.LatLngExpression, options?: GeodesicOptions)
54```
55
56Both classes are extended from [L.Polyline](http://leafletjs.com/reference.html#polyline), so all methods, events and options for `L.Polyline` can be used with `L.Geodesic` and `L.GeodesicCircle` here as well.
57
58## Geodesic Lines
59
60This draws a line. The geometry (points) to use can be given during creation as:
61
62### Objects (Literals)
63
64```JavaScript
65const Berlin = {lat: 52.5, lng: 13.35};
66const LosAngeles = {lat: 33.82, lng: -118.38};
67const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
68```
69
70### LatLng-Class
71
72```JavaScript
73const Berlin = new L.LatLng(52.5, 13.35);
74const LosAngeles = new L.LatLng(33.82, -118.38);
75const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
76```
77
78### Tuples
79
80```JavaScript
81const Berlin = [52.5, 13.35];
82const LosAngeles = [33.82, -118.38];
83const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
84```
85
86![line](docs/img/line.png)
87
88### Line-strings
89
90Multiple consecutive points can be given as an array (linestring):
91
92```JavaScript
93const places = [
94 new L.LatLng(52.5, 13.35), // Berlin
95 new L.LatLng(33.82, -118.38), // Los Angeles
96 new L.LatLng(-33.44, -70.71), // Santiago
97 new L.LatLng(-33.94, 18.39), // Capetown
98];
99const geodesic = new L.Geodesic(places).addTo(map);
100```
101
102![linestring](docs/img/linestring.png)
103
104### Multi-line-strings
105
106Multiple independent linestrings can be defined as a 2-dimensional array of points:
107
108```JavaScript
109const places = [
110 [ // 1st line
111 new L.LatLng(52.5, 13.35), // Berlin
112 new L.LatLng(33.82, -118.38), // Los Angeles
113 ],
114 [ // 2nd line
115 new L.LatLng(-33.44, -70.71), // Santiago
116 new L.LatLng(-33.94, 18.39), // Capetown
117 ]
118];
119const geodesic = new L.Geodesic(places).addTo(map);
120```
121
122![multilinestring](docs/img/multilinestring.png)
123
124### GeoJSON-Support
125
126GeoJSON-data can be used to create geodesic lines with the `fromGeoJson()` method:
127
128```JavaScript
129const geojson = {
130 "type": "LineString",
131 "coordinates": [
132 [13.35, 52.5], [-122.33, 47.56], [18.39, -33.94], [116.39, 39.92], [13.35, 52.5]
133 ]
134};
135const geodesic = new L.Geodesic().addTo(map);
136geodesic.fromGeoJson(geojson);
137```
138
139![geojson](docs/img/geojson.png)
140
141### Updating the geometry
142
143#### Set new geometry
144
145The Geodesic-Class provides a `setLatLngs()`-Method, that can be used to update the geometry of an existing `L.Geodesic`-object:
146
147```Javascript
148const geodesic = new L.Geodesic().addTo(map); // add empty object to the map
149
150const Berlin = new L.LatLng(52.5, 13.35);
151const LosAngeles = new L.LatLng(33.82, -118.38);
152
153geodesic.setLatLngs([Berlin, LosAngeles]) // update in-place
154```
155
156The `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.
157
158#### Delete geometry
159
160Delete the existing geometry by setting an empty array `geodesic.setLatLngs([])`.
161
162#### adding points
163
164Points can be added to existing geodesic lines with `addLatLng()`:
165
166```Javascript
167const Berlin = new L.LatLng(52.5, 13.35);
168const LosAngeles = new L.LatLng(33.82, -118.38);
169const Beijing = new L.LatLng(39.92, 116.39);
170
171const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
172geodesic.addLatLng(Beijing); // results in [[Berlin, LosAngeles, Beijing]
173```
174
175The 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:
176
177```Javascript
178const Berlin = new L.LatLng(52.5, 13.35);
179const LosAngeles = new L.LatLng(33.82, -118.38);
180const Beijing = new L.LatLng(39.92, 116.39 );
181const Capetown = new L.LatLng(-33.94, 18.39 );
182const Santiago = new L.LatLng(-33.44, -70.71);
183
184const geodesic = new L.Geodesic([[Berlin, LosAngeles], [Santiago, Capetown]]).addTo(map);
185geodesic.addLatLng(Beijing, geodesic.points[0]); // results in [[Berlin, LosAngeles, Beijing], [Santiago, Capetown]]
186```
187
188### Line Options
189All options defined for [Polyline](http://leafletjs.com/reference.html#polyline) and [Path](https://leafletjs.com/reference.html#path) for can be used Leaflet.Geodesic.
190
191The most important options are:
192
193Option | Type | Default | Description
194---|---|---|---
195`color` | `String` | "#3388ff" | Stroke color
196`weight` | `Number` | 3 | Stroke width in pixels
197`opacity` | `Number` | 1.0 | Stroke opacity (0=transparent, 1=opaque)
198`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
199`wrap` | `Boolean` | true | Wrap geodesic line at antimeridian. Set to `false`, to draw a line over the antimeridian. See [no-wrap demo](https://blog.cyclemap.link/Leaflet.Geodesic/nowrap-interactive.html) for example.
200
201Example:
202
203```Javascript
204const Berlin = new L.LatLng(52.5, 13.35);
205const LosAngeles = new L.LatLng(33.82, -118.38);
206const options = {
207 weight: 20,
208 opacity: 0.5,
209 color: 'red',
210};
211const geodesic = new L.Geodesic([Berlin, LosAngeles], options).addTo(map);
212```
213
214![lineoptions](docs/img/lineoptions.png)
215
216## Geodesic Circles
217
218Circles can be added with another class called `L.GeodesicCircle` as follows:
219
220```Javascript
221const Seattle = new L.LatLng(47.56, -122.33);
222const geodesiccircle = new L.GeodesicCircle(Seattle, {
223 radius: 3000*1000, // 3000km in meters
224}).addTo(map);
225```
226
227![circle](docs/img/circle.png)
228
229The geometry of a circle can be updated with the following methods:
230
231- `setLatLng(latlng: L.LatLngExpression)` - set a new center
232- `setRadius(radius: number)` - update the radius
233
234Handling of **filled** circles crossing the antimeridian (wrapping) is not yet supported. Set `fill: false` in these cases to avoid display artefacts.
235
236### Circle Options
237
238Option | Type | Default | Description
239---|---|---|---
240`radius` | `Number` | 1000*1000 | Radius in **meters**
241`steps` | `Number` | 24 | Number of segments that are used to approximate the circle.
242`fill` | `boolean` | true | Draws a filled circle.
243`color` | `String` | "#3388ff" | Stroke color
244`weight` | `Number` | 3 | Stroke width in pixels
245`opacity` | `Number` | 1.0 | Stroke opacity (0=transparent, 1=opaque)
246
247Please refer to the options for [Polyline](http://leafletjs.com/reference.html#polyline) and [Path](https://leafletjs.com/reference.html#path) for additional settings.
248
249## Statistics
250
251The `L.Geodesic` and `L.GeodesicCircle`-class provide a `statistics`-Object with the following properties:
252
253Property | Type | Description
254---|---|---
255`totalDistance` | `Number` | The total distance of all geodesic lines in meters. (Circumfence for `L.GeodesicCircle`)
256`distanceArray` | `Number[]` | The distance for each separate linestring in meters
257`points` | `Number` | Number of points that were given on creation or with `setLatLngs()`
258`vertices` | `Number` | Number of vertices of all geodesic lines that were calculated
259
260## Distance Calculation
261
262The `L.Geodesic` provides a `distance`-function to calculate the precise distance between two points:
263
264```Javascript
265const Berlin = new L.LatLng(52.5, 13.35);
266const Beijing = new L.LatLng(39.92, 116.39);
267
268const line = new L.Geodesic();
269const distance = line.distance(Berlin, Beijing);
270console.log(`${Math.floor(distance/1000)} km`) // prints: 7379 km
271```
272
273The `L.GeodesicCircle`-class provides a `distanceTo`-function to calculate the distance between the current center and any given point:
274
275```Javascript
276const Berlin = new L.LatLng(52.5, 13.35);
277const Beijing = new L.LatLng(39.92, 116.39);
278
279const circle = new L.GeodesicCircle(Berlin);
280const distance = circle.distanceTo(Beijing);
281console.log(`${Math.floor(distance/1000)} km`) // prints: 7379 km
282```
283
284## Scientific background
285
286All calculations are based on the [WGS84-Ellipsoid](https://en.wikipedia.org/wiki/World_Geodetic_System#WGS84) (EPSG:4326) using [Vincenty's formulae](https://en.wikipedia.org/wiki/Vincenty%27s_formulae). This method leads to very precise calculations but may fail for some corner-cases (e.g. [Antipodes](https://en.wikipedia.org/wiki/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...